highlighter_controller_unittest.cc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
  1. // Copyright 2017 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/highlighter/highlighter_controller.h"
  5. #include <memory>
  6. #include "ash/assistant/test/assistant_ash_test_base.h"
  7. #include "ash/fast_ink/fast_ink_points.h"
  8. #include "ash/highlighter/highlighter_controller_test_api.h"
  9. #include "ash/public/cpp/stylus_utils.h"
  10. #include "ash/shell.h"
  11. #include "ash/system/palette/mock_palette_tool_delegate.h"
  12. #include "ash/system/palette/palette_tool.h"
  13. #include "ash/system/palette/tools/metalayer_mode.h"
  14. #include "base/strings/stringprintf.h"
  15. #include "build/build_config.h"
  16. #include "ui/aura/window_tree_host.h"
  17. #include "ui/compositor/test/draw_waiter_for_test.h"
  18. #include "ui/events/test/event_generator.h"
  19. namespace ash {
  20. namespace {
  21. class TestHighlighterObserver : public HighlighterController::Observer {
  22. public:
  23. TestHighlighterObserver() = default;
  24. TestHighlighterObserver(const TestHighlighterObserver&) = delete;
  25. TestHighlighterObserver& operator=(const TestHighlighterObserver&) = delete;
  26. ~TestHighlighterObserver() override = default;
  27. // HighlighterController::Observer:
  28. void OnHighlighterEnabledChanged(HighlighterEnabledState state) override {
  29. switch (state) {
  30. case HighlighterEnabledState::kEnabled:
  31. ++enabled_count_;
  32. break;
  33. case HighlighterEnabledState::kDisabledByUser:
  34. ++disabled_by_user_count_;
  35. break;
  36. case HighlighterEnabledState::kDisabledBySessionAbort:
  37. ++disabled_by_session_abort_;
  38. break;
  39. case HighlighterEnabledState::kDisabledBySessionComplete:
  40. ++disabled_by_session_complete_;
  41. break;
  42. }
  43. }
  44. void OnHighlighterSelectionRecognized(const gfx::Rect& rect) override {
  45. last_recognized_rect_ = rect;
  46. }
  47. int enabled_count_ = 0;
  48. int disabled_by_user_count_ = 0;
  49. int disabled_by_session_abort_ = 0;
  50. int disabled_by_session_complete_ = 0;
  51. gfx::Rect last_recognized_rect_;
  52. };
  53. class HighlighterControllerTest : public AssistantAshTestBase {
  54. public:
  55. HighlighterControllerTest() = default;
  56. HighlighterControllerTest(const HighlighterControllerTest&) = delete;
  57. HighlighterControllerTest& operator=(const HighlighterControllerTest&) =
  58. delete;
  59. ~HighlighterControllerTest() override = default;
  60. void SetUp() override {
  61. AssistantAshTestBase::SetUp();
  62. controller_ = Shell::Get()->highlighter_controller();
  63. controller_test_api_ =
  64. std::make_unique<HighlighterControllerTestApi>(controller_);
  65. palette_tool_delegate_ = std::make_unique<MockPaletteToolDelegate>();
  66. tool_ = std::make_unique<MetalayerMode>(palette_tool_delegate_.get());
  67. }
  68. void TearDown() override {
  69. tool_.reset();
  70. // This needs to be called first to reset the controller state before the
  71. // shell instance gets torn down.
  72. controller_test_api_.reset();
  73. AssistantAshTestBase::TearDown();
  74. }
  75. void UpdateDisplayAndWaitForCompositingEnded(
  76. const std::string& display_specs) {
  77. UpdateDisplay(display_specs);
  78. ui::DrawWaiterForTest::WaitForCompositingEnded(
  79. Shell::GetPrimaryRootWindow()->GetHost()->compositor());
  80. }
  81. protected:
  82. void TraceRect(const gfx::Rect& rect) {
  83. ui::test::EventGenerator* event_generator = GetEventGenerator();
  84. event_generator->MoveTouch(gfx::Point(rect.x(), rect.y()));
  85. event_generator->PressTouch();
  86. event_generator->MoveTouch(gfx::Point(rect.right(), rect.y()));
  87. event_generator->MoveTouch(gfx::Point(rect.right(), rect.bottom()));
  88. event_generator->MoveTouch(gfx::Point(rect.x(), rect.bottom()));
  89. event_generator->MoveTouch(gfx::Point(rect.x(), rect.y()));
  90. event_generator->ReleaseTouch();
  91. // The the events above will trigger a frame, so wait until a new
  92. // CompositorFrame is generated before terminating.
  93. ui::DrawWaiterForTest::WaitForCompositingEnded(
  94. Shell::GetPrimaryRootWindow()->GetHost()->compositor());
  95. }
  96. std::unique_ptr<HighlighterControllerTestApi> controller_test_api_;
  97. std::unique_ptr<MockPaletteToolDelegate> palette_tool_delegate_;
  98. std::unique_ptr<PaletteTool> tool_;
  99. HighlighterController* controller_ = nullptr; // Not owned.
  100. };
  101. } // namespace
  102. // Test to ensure the class responsible for drawing the highlighter pointer
  103. // receives points from stylus movements as expected.
  104. TEST_F(HighlighterControllerTest, HighlighterRenderer) {
  105. ash::stylus_utils::SetHasStylusInputForTesting();
  106. // The highlighter pointer mode only works with stylus.
  107. ui::test::EventGenerator* event_generator = GetEventGenerator();
  108. event_generator->EnterPenPointerMode();
  109. // When disabled the highlighter pointer should not be showing.
  110. event_generator->MoveTouch(gfx::Point(1, 1));
  111. EXPECT_FALSE(controller_test_api_->IsShowingHighlighter());
  112. // Verify that by enabling the mode, the highlighter pointer should still not
  113. // be showing.
  114. controller_test_api_->SetEnabled(true);
  115. EXPECT_FALSE(controller_test_api_->IsShowingHighlighter());
  116. // Verify moving the stylus 4 times will not display the highlighter pointer.
  117. event_generator->MoveTouch(gfx::Point(2, 2));
  118. event_generator->MoveTouch(gfx::Point(3, 3));
  119. event_generator->MoveTouch(gfx::Point(4, 4));
  120. event_generator->MoveTouch(gfx::Point(5, 5));
  121. EXPECT_FALSE(controller_test_api_->IsShowingHighlighter());
  122. // Verify pressing the stylus will show the highlighter pointer and add a
  123. // point but will not activate fading out.
  124. event_generator->PressTouch();
  125. EXPECT_TRUE(controller_test_api_->IsShowingHighlighter());
  126. EXPECT_FALSE(controller_test_api_->IsFadingAway());
  127. EXPECT_EQ(1, controller_test_api_->points().GetNumberOfPoints());
  128. // Verify dragging the stylus 2 times will add 2 more points.
  129. event_generator->MoveTouch(gfx::Point(6, 6));
  130. event_generator->MoveTouch(gfx::Point(7, 7));
  131. EXPECT_EQ(3, controller_test_api_->points().GetNumberOfPoints());
  132. // Verify releasing the stylus still shows the highlighter pointer, which is
  133. // fading away.
  134. event_generator->ReleaseTouch();
  135. EXPECT_TRUE(controller_test_api_->IsShowingHighlighter());
  136. EXPECT_TRUE(controller_test_api_->IsFadingAway());
  137. // Verify that disabling the mode right after the gesture completion does not
  138. // hide the highlighter pointer immediately but lets it play out the
  139. // animation.
  140. controller_test_api_->SetEnabled(false);
  141. EXPECT_TRUE(controller_test_api_->IsShowingHighlighter());
  142. EXPECT_TRUE(controller_test_api_->IsFadingAway());
  143. // Verify that disabling the mode mid-gesture hides the highlighter pointer
  144. // immediately.
  145. controller_test_api_->DestroyPointerView();
  146. controller_test_api_->SetEnabled(true);
  147. event_generator->PressTouch();
  148. event_generator->MoveTouch(gfx::Point(6, 6));
  149. EXPECT_TRUE(controller_test_api_->IsShowingHighlighter());
  150. controller_test_api_->SetEnabled(false);
  151. EXPECT_FALSE(controller_test_api_->IsShowingHighlighter());
  152. // Verify that the highlighter pointer does not add points while disabled.
  153. event_generator->PressTouch();
  154. event_generator->MoveTouch(gfx::Point(8, 8));
  155. event_generator->ReleaseTouch();
  156. event_generator->MoveTouch(gfx::Point(9, 9));
  157. EXPECT_FALSE(controller_test_api_->IsShowingHighlighter());
  158. // Verify that the highlighter pointer does not get shown if points are not
  159. // coming from the stylus, even when enabled.
  160. event_generator->ExitPenPointerMode();
  161. controller_test_api_->SetEnabled(true);
  162. event_generator->PressTouch();
  163. event_generator->MoveTouch(gfx::Point(10, 10));
  164. event_generator->MoveTouch(gfx::Point(11, 11));
  165. EXPECT_FALSE(controller_test_api_->IsShowingHighlighter());
  166. event_generator->ReleaseTouch();
  167. }
  168. // Test to ensure the class responsible for drawing the highlighter pointer
  169. // handles prediction as expected when it receives points from stylus movements.
  170. TEST_F(HighlighterControllerTest, HighlighterPrediction) {
  171. controller_test_api_->SetEnabled(true);
  172. // The highlighter pointer mode only works with stylus.
  173. ui::test::EventGenerator* event_generator = GetEventGenerator();
  174. event_generator->EnterPenPointerMode();
  175. event_generator->PressTouch();
  176. EXPECT_TRUE(controller_test_api_->IsShowingHighlighter());
  177. EXPECT_EQ(1, controller_test_api_->points().GetNumberOfPoints());
  178. // Initial press event shouldn't generate any predicted points as there's no
  179. // history to use for prediction.
  180. EXPECT_EQ(0, controller_test_api_->predicted_points().GetNumberOfPoints());
  181. // Verify dragging the stylus 3 times will add some predicted points.
  182. event_generator->MoveTouch(gfx::Point(10, 10));
  183. event_generator->MoveTouch(gfx::Point(20, 20));
  184. event_generator->MoveTouch(gfx::Point(30, 30));
  185. EXPECT_NE(0, controller_test_api_->predicted_points().GetNumberOfPoints());
  186. // Verify predicted points are in the right direction.
  187. for (const auto& point : controller_test_api_->predicted_points().points()) {
  188. EXPECT_LT(30, point.location.x());
  189. EXPECT_LT(30, point.location.y());
  190. }
  191. }
  192. // Test that stylus gestures are correctly recognized by HighlighterController.
  193. #if BUILDFLAG(IS_CHROMEOS_ASH)
  194. #define MAYBE_HighlighterGestures DISABLED_HighlighterGestures
  195. #else
  196. #define MAYBE_HighlighterGestures HighlighterGestures
  197. #endif
  198. TEST_F(HighlighterControllerTest, MAYBE_HighlighterGestures) {
  199. controller_test_api_->SetEnabled(true);
  200. ui::test::EventGenerator* event_generator = GetEventGenerator();
  201. event_generator->EnterPenPointerMode();
  202. TestHighlighterObserver observer;
  203. controller_->AddObserver(&observer);
  204. // A non-horizontal stroke is not recognized
  205. controller_test_api_->ResetSelection();
  206. event_generator->MoveTouch(gfx::Point(100, 100));
  207. event_generator->PressTouch();
  208. event_generator->MoveTouch(gfx::Point(200, 200));
  209. event_generator->ReleaseTouch();
  210. EXPECT_FALSE(controller_test_api_->HandleSelectionCalled());
  211. // An almost horizontal stroke is recognized
  212. controller_test_api_->ResetSelection();
  213. event_generator->MoveTouch(gfx::Point(100, 100));
  214. event_generator->PressTouch();
  215. event_generator->MoveTouch(gfx::Point(300, 102));
  216. event_generator->ReleaseTouch();
  217. EXPECT_TRUE(controller_test_api_->HandleSelectionCalled());
  218. // Horizontal stroke selection rectangle should:
  219. // have the same horizontal center line as the stroke bounding box,
  220. // be 4dp wider than the stroke bounding box,
  221. // be exactly 14dp high.
  222. gfx::Rect expected_rect(98, 94, 204, 14);
  223. EXPECT_EQ(expected_rect, controller_test_api_->selection());
  224. EXPECT_EQ(expected_rect, observer.last_recognized_rect_);
  225. // An insufficiently closed C-like shape is not recognized
  226. controller_test_api_->ResetSelection();
  227. event_generator->MoveTouch(gfx::Point(100, 0));
  228. event_generator->PressTouch();
  229. event_generator->MoveTouch(gfx::Point(0, 0));
  230. event_generator->MoveTouch(gfx::Point(0, 100));
  231. event_generator->MoveTouch(gfx::Point(100, 100));
  232. event_generator->ReleaseTouch();
  233. EXPECT_FALSE(controller_test_api_->HandleSelectionCalled());
  234. // An almost closed G-like shape is recognized
  235. controller_test_api_->ResetSelection();
  236. event_generator->MoveTouch(gfx::Point(200, 0));
  237. event_generator->PressTouch();
  238. event_generator->MoveTouch(gfx::Point(0, 0));
  239. event_generator->MoveTouch(gfx::Point(0, 100));
  240. event_generator->MoveTouch(gfx::Point(200, 100));
  241. event_generator->MoveTouch(gfx::Point(200, 20));
  242. event_generator->ReleaseTouch();
  243. EXPECT_TRUE(controller_test_api_->HandleSelectionCalled());
  244. expected_rect = gfx::Rect(0, 0, 200, 100);
  245. EXPECT_EQ(expected_rect, controller_test_api_->selection());
  246. EXPECT_EQ(expected_rect, observer.last_recognized_rect_);
  247. // A closed diamond shape is recognized
  248. controller_test_api_->ResetSelection();
  249. event_generator->MoveTouch(gfx::Point(100, 50));
  250. event_generator->PressTouch();
  251. event_generator->MoveTouch(gfx::Point(200, 150));
  252. event_generator->MoveTouch(gfx::Point(100, 250));
  253. event_generator->MoveTouch(gfx::Point(0, 150));
  254. event_generator->MoveTouch(gfx::Point(100, 50));
  255. event_generator->ReleaseTouch();
  256. EXPECT_TRUE(controller_test_api_->HandleSelectionCalled());
  257. expected_rect = gfx::Rect(0, 50, 200, 200);
  258. EXPECT_EQ(expected_rect, controller_test_api_->selection());
  259. EXPECT_EQ(expected_rect, observer.last_recognized_rect_);
  260. controller_->RemoveObserver(&observer);
  261. }
  262. // TODO(1346951): Disable HighlighterGesturesScaled on Linux Chromium OS Asan
  263. // Lsan Tests.
  264. #if defined(LEAK_SANITIZER) && BUILDFLAG(IS_CHROMEOS)
  265. #define MAYBE_HighlighterGesturesScaled DISABLED_HighlighterGesturesScaled
  266. #else
  267. #define MAYBE_HighlighterGesturesScaled HighlighterGesturesScaled
  268. #endif
  269. TEST_F(HighlighterControllerTest, MAYBE_HighlighterGesturesScaled) {
  270. controller_test_api_->SetEnabled(true);
  271. ui::test::EventGenerator* event_generator = GetEventGenerator();
  272. event_generator->EnterPenPointerMode();
  273. const gfx::Rect original_px(200, 100, 400, 300);
  274. constexpr float display_scales[] = {1.f, 1.5f, 2.0f};
  275. constexpr float ui_scales[] = {0.5f, 0.67f, 1.0f, 1.25f,
  276. 1.33f, 1.5f, 1.67f, 2.0f};
  277. for (size_t i = 0; i < sizeof(display_scales) / sizeof(float); ++i) {
  278. const float display_scale = display_scales[i];
  279. for (size_t j = 0; j < sizeof(ui_scales) / sizeof(float); ++j) {
  280. const float ui_scale = ui_scales[j];
  281. std::string display_spec =
  282. base::StringPrintf("1500x1000*%.2f@%.2f", display_scale, ui_scale);
  283. SCOPED_TRACE(display_spec);
  284. UpdateDisplayAndWaitForCompositingEnded(display_spec);
  285. controller_test_api_->ResetSelection();
  286. TraceRect(original_px);
  287. EXPECT_TRUE(controller_test_api_->HandleSelectionCalled());
  288. const float combined_scale = display_scale * ui_scale;
  289. const gfx::Rect selection_dp = controller_test_api_->selection();
  290. const gfx::Rect selection_px = gfx::ToEnclosingRect(
  291. gfx::ScaleRect(gfx::RectF(selection_dp), combined_scale));
  292. EXPECT_TRUE(selection_px.Contains(original_px));
  293. gfx::Rect inflated_px(original_px);
  294. // Allow for rounding errors within 1dp.
  295. const int error_margin = static_cast<int>(std::ceil(combined_scale));
  296. inflated_px.Inset(-error_margin);
  297. EXPECT_TRUE(inflated_px.Contains(selection_px));
  298. }
  299. }
  300. }
  301. // Test that stylus gesture recognition correctly handles display rotation
  302. TEST_F(HighlighterControllerTest, HighlighterGesturesRotated) {
  303. controller_test_api_->SetEnabled(true);
  304. ui::test::EventGenerator* event_generator = GetEventGenerator();
  305. event_generator->EnterPenPointerMode();
  306. const gfx::Rect trace(200, 100, 400, 300);
  307. // No rotation
  308. UpdateDisplayAndWaitForCompositingEnded("1500x1000");
  309. controller_test_api_->ResetSelection();
  310. TraceRect(trace);
  311. EXPECT_TRUE(controller_test_api_->HandleSelectionCalled());
  312. EXPECT_EQ("200,100 400x300", controller_test_api_->selection().ToString());
  313. // Rotate to 90 degrees
  314. UpdateDisplayAndWaitForCompositingEnded("1500x1000/r");
  315. controller_test_api_->ResetSelection();
  316. TraceRect(trace);
  317. EXPECT_TRUE(controller_test_api_->HandleSelectionCalled());
  318. EXPECT_EQ("100,900 300x400", controller_test_api_->selection().ToString());
  319. // Rotate to 180 degrees
  320. UpdateDisplayAndWaitForCompositingEnded("1500x1000/u");
  321. controller_test_api_->ResetSelection();
  322. TraceRect(trace);
  323. EXPECT_TRUE(controller_test_api_->HandleSelectionCalled());
  324. EXPECT_EQ("900,600 400x300", controller_test_api_->selection().ToString());
  325. // Rotate to 270 degrees
  326. UpdateDisplayAndWaitForCompositingEnded("1500x1000/l");
  327. controller_test_api_->ResetSelection();
  328. TraceRect(trace);
  329. EXPECT_TRUE(controller_test_api_->HandleSelectionCalled());
  330. EXPECT_EQ("600,200 300x400", controller_test_api_->selection().ToString());
  331. }
  332. // Flaky on Linux Chromium OS ASan LSan. https://crbug.com/1315061
  333. #if defined(ADDRESS_SANITIZER)
  334. #define MAYBE_InterruptedStroke DISABLED_InterruptedStroke
  335. #else
  336. #define MAYBE_InterruptedStroke InterruptedStroke
  337. #endif
  338. // Test that a stroke interrupted close to the screen edge is treated as
  339. // contiguous.
  340. TEST_F(HighlighterControllerTest, MAYBE_InterruptedStroke) {
  341. controller_test_api_->SetEnabled(true);
  342. ui::test::EventGenerator* event_generator = GetEventGenerator();
  343. event_generator->EnterPenPointerMode();
  344. UpdateDisplayAndWaitForCompositingEnded("1500x1000");
  345. // An interrupted stroke close to the screen edge should be recognized as a
  346. // contiguous stroke.
  347. controller_test_api_->ResetSelection();
  348. event_generator->MoveTouch(gfx::Point(300, 100));
  349. event_generator->PressTouch();
  350. event_generator->MoveTouch(gfx::Point(0, 100));
  351. event_generator->ReleaseTouch();
  352. EXPECT_TRUE(controller_test_api_->IsWaitingToResumeStroke());
  353. EXPECT_FALSE(controller_test_api_->HandleSelectionCalled());
  354. EXPECT_FALSE(controller_test_api_->IsFadingAway());
  355. event_generator->MoveTouch(gfx::Point(0, 200));
  356. event_generator->PressTouch();
  357. event_generator->MoveTouch(gfx::Point(300, 200));
  358. event_generator->ReleaseTouch();
  359. EXPECT_FALSE(controller_test_api_->IsWaitingToResumeStroke());
  360. EXPECT_TRUE(controller_test_api_->HandleSelectionCalled());
  361. EXPECT_EQ("0,100 300x100", controller_test_api_->selection().ToString());
  362. // Repeat the same gesture, but simulate a timeout after the gap. This should
  363. // force the gesture completion.
  364. controller_test_api_->ResetSelection();
  365. event_generator->MoveTouch(gfx::Point(300, 100));
  366. event_generator->PressTouch();
  367. event_generator->MoveTouch(gfx::Point(0, 100));
  368. event_generator->ReleaseTouch();
  369. EXPECT_TRUE(controller_test_api_->IsWaitingToResumeStroke());
  370. EXPECT_FALSE(controller_test_api_->HandleSelectionCalled());
  371. EXPECT_FALSE(controller_test_api_->IsFadingAway());
  372. controller_test_api_->SimulateInterruptedStrokeTimeout();
  373. EXPECT_FALSE(controller_test_api_->IsWaitingToResumeStroke());
  374. EXPECT_TRUE(controller_test_api_->HandleSelectionCalled());
  375. EXPECT_TRUE(controller_test_api_->IsFadingAway());
  376. }
  377. // Test that the selection is never crossing the screen bounds.
  378. // Flaky, https://crbug.com/1311772
  379. TEST_F(HighlighterControllerTest, DISABLED_SelectionInsideScreen) {
  380. controller_test_api_->SetEnabled(true);
  381. ui::test::EventGenerator* event_generator = GetEventGenerator();
  382. event_generator->EnterPenPointerMode();
  383. constexpr float display_scales[] = {1.f, 1.5f, 2.0f};
  384. for (size_t i = 0; i < sizeof(display_scales) / sizeof(float); ++i) {
  385. // 2nd display is for offscreen test.
  386. std::string display_spec = base::StringPrintf(
  387. "1000x999*%.2f,500x1000*%.2f", display_scales[i], display_scales[i]);
  388. SCOPED_TRACE(display_spec);
  389. UpdateDisplayAndWaitForCompositingEnded(display_spec);
  390. const gfx::Rect screen(0, 0, 1000, 999);
  391. // Rectangle completely offscreen.
  392. controller_test_api_->ResetSelection();
  393. TraceRect(gfx::Rect(-100, -100, 10, 10));
  394. controller_test_api_->SimulateInterruptedStrokeTimeout();
  395. EXPECT_FALSE(controller_test_api_->HandleSelectionCalled());
  396. // Rectangle crossing the left edge.
  397. controller_test_api_->ResetSelection();
  398. TraceRect(gfx::Rect(-100, 100, 200, 200));
  399. controller_test_api_->SimulateInterruptedStrokeTimeout();
  400. EXPECT_TRUE(controller_test_api_->HandleSelectionCalled());
  401. EXPECT_TRUE(screen.Contains(controller_test_api_->selection()));
  402. // Rectangle crossing the top edge.
  403. controller_test_api_->ResetSelection();
  404. TraceRect(gfx::Rect(100, -100, 200, 200));
  405. controller_test_api_->SimulateInterruptedStrokeTimeout();
  406. EXPECT_TRUE(controller_test_api_->HandleSelectionCalled());
  407. EXPECT_TRUE(screen.Contains(controller_test_api_->selection()));
  408. // Rectangle crossing the right edge.
  409. controller_test_api_->ResetSelection();
  410. TraceRect(gfx::Rect(900, 100, 200, 200));
  411. controller_test_api_->SimulateInterruptedStrokeTimeout();
  412. EXPECT_TRUE(controller_test_api_->HandleSelectionCalled());
  413. EXPECT_TRUE(screen.Contains(controller_test_api_->selection()));
  414. // Rectangle crossing the bottom edge.
  415. controller_test_api_->ResetSelection();
  416. TraceRect(gfx::Rect(100, 900, 200, 200));
  417. controller_test_api_->SimulateInterruptedStrokeTimeout();
  418. EXPECT_TRUE(controller_test_api_->HandleSelectionCalled());
  419. EXPECT_TRUE(screen.Contains(controller_test_api_->selection()));
  420. // Vertical stroke completely offscreen.
  421. controller_test_api_->ResetSelection();
  422. event_generator->MoveTouch(gfx::Point(1100, 100));
  423. event_generator->PressTouch();
  424. event_generator->MoveTouch(gfx::Point(1100, 500));
  425. event_generator->ReleaseTouch();
  426. controller_test_api_->SimulateInterruptedStrokeTimeout();
  427. EXPECT_FALSE(controller_test_api_->HandleSelectionCalled());
  428. // Horizontal stroke along the top edge of the screen.
  429. controller_test_api_->ResetSelection();
  430. event_generator->MoveTouch(gfx::Point(0, 0));
  431. event_generator->PressTouch();
  432. event_generator->MoveTouch(gfx::Point(1000, 0));
  433. event_generator->ReleaseTouch();
  434. controller_test_api_->SimulateInterruptedStrokeTimeout();
  435. EXPECT_TRUE(controller_test_api_->HandleSelectionCalled());
  436. EXPECT_TRUE(screen.Contains(controller_test_api_->selection()));
  437. // Horizontal stroke along the bottom edge of the screen.
  438. controller_test_api_->ResetSelection();
  439. event_generator->MoveTouch(gfx::Point(0, 999));
  440. event_generator->PressTouch();
  441. event_generator->MoveTouch(gfx::Point(1000, 999));
  442. event_generator->ReleaseTouch();
  443. controller_test_api_->SimulateInterruptedStrokeTimeout();
  444. EXPECT_TRUE(controller_test_api_->HandleSelectionCalled());
  445. EXPECT_TRUE(screen.Contains(controller_test_api_->selection()));
  446. }
  447. }
  448. // Test that a detached client does not receive notifications.
  449. TEST_F(HighlighterControllerTest, DetachedClient) {
  450. controller_test_api_->SetEnabled(true);
  451. ui::test::EventGenerator* event_generator = GetEventGenerator();
  452. event_generator->EnterPenPointerMode();
  453. UpdateDisplayAndWaitForCompositingEnded("1500x1000");
  454. const gfx::Rect trace(200, 100, 400, 300);
  455. // Detach the client, no notifications should reach it.
  456. controller_test_api_->DetachClient();
  457. controller_test_api_->ResetEnabledState();
  458. controller_test_api_->SetEnabled(false);
  459. EXPECT_FALSE(controller_test_api_->HandleEnabledStateChangedCalled());
  460. controller_test_api_->SetEnabled(true);
  461. EXPECT_FALSE(controller_test_api_->HandleEnabledStateChangedCalled());
  462. controller_test_api_->ResetSelection();
  463. TraceRect(trace);
  464. EXPECT_FALSE(controller_test_api_->HandleSelectionCalled());
  465. // Attach the client again, notifications should be delivered normally.
  466. controller_test_api_->AttachClient();
  467. controller_test_api_->ResetEnabledState();
  468. controller_test_api_->SetEnabled(false);
  469. EXPECT_TRUE(controller_test_api_->HandleEnabledStateChangedCalled());
  470. controller_test_api_->SetEnabled(true);
  471. EXPECT_TRUE(controller_test_api_->HandleEnabledStateChangedCalled());
  472. controller_test_api_->ResetSelection();
  473. TraceRect(trace);
  474. EXPECT_TRUE(controller_test_api_->HandleSelectionCalled());
  475. }
  476. // Test enabling/disabling metalayer mode by selecting/deselecting on palette
  477. // tool and calling UpdateEnabledState notify observers properly.
  478. TEST_F(HighlighterControllerTest, UpdateEnabledState) {
  479. TestHighlighterObserver observer;
  480. controller_->AddObserver(&observer);
  481. // Assert initial state.
  482. ASSERT_EQ(0, observer.enabled_count_);
  483. ASSERT_EQ(0, observer.disabled_by_user_count_);
  484. ASSERT_EQ(0, observer.disabled_by_session_abort_);
  485. ASSERT_EQ(0, observer.disabled_by_session_complete_);
  486. // Test enabling.
  487. tool_->OnEnable();
  488. EXPECT_EQ(1, observer.enabled_count_);
  489. EXPECT_EQ(0, observer.disabled_by_user_count_);
  490. EXPECT_EQ(0, observer.disabled_by_session_abort_);
  491. EXPECT_EQ(0, observer.disabled_by_session_complete_);
  492. // Test disabling by user.
  493. tool_->OnDisable();
  494. EXPECT_EQ(1, observer.enabled_count_);
  495. EXPECT_EQ(1, observer.disabled_by_user_count_);
  496. EXPECT_EQ(0, observer.disabled_by_session_abort_);
  497. EXPECT_EQ(0, observer.disabled_by_session_complete_);
  498. // Test disabling by session abort.
  499. tool_->OnEnable();
  500. EXPECT_EQ(2, observer.enabled_count_);
  501. EXPECT_EQ(1, observer.disabled_by_user_count_);
  502. EXPECT_EQ(0, observer.disabled_by_session_abort_);
  503. EXPECT_EQ(0, observer.disabled_by_session_complete_);
  504. controller_->UpdateEnabledState(
  505. HighlighterEnabledState::kDisabledBySessionAbort);
  506. EXPECT_EQ(2, observer.enabled_count_);
  507. EXPECT_EQ(1, observer.disabled_by_user_count_);
  508. EXPECT_EQ(1, observer.disabled_by_session_abort_);
  509. EXPECT_EQ(0, observer.disabled_by_session_complete_);
  510. // Test disabling by session complete.
  511. tool_->OnEnable();
  512. EXPECT_EQ(3, observer.enabled_count_);
  513. EXPECT_EQ(1, observer.disabled_by_user_count_);
  514. EXPECT_EQ(1, observer.disabled_by_session_abort_);
  515. EXPECT_EQ(0, observer.disabled_by_session_complete_);
  516. controller_->UpdateEnabledState(
  517. HighlighterEnabledState::kDisabledBySessionComplete);
  518. EXPECT_EQ(3, observer.enabled_count_);
  519. EXPECT_EQ(1, observer.disabled_by_user_count_);
  520. EXPECT_EQ(1, observer.disabled_by_session_abort_);
  521. EXPECT_EQ(1, observer.disabled_by_session_complete_);
  522. controller_->RemoveObserver(&observer);
  523. }
  524. // Test aborting a metalayer session and notifying observers properly.
  525. TEST_F(HighlighterControllerTest, AbortSession) {
  526. TestHighlighterObserver observer;
  527. controller_->AddObserver(&observer);
  528. // Assert initial state.
  529. ASSERT_EQ(0, observer.enabled_count_);
  530. ASSERT_EQ(0, observer.disabled_by_user_count_);
  531. ASSERT_EQ(0, observer.disabled_by_session_abort_);
  532. ASSERT_EQ(0, observer.disabled_by_session_complete_);
  533. // Start metalayer session.
  534. tool_->OnEnable();
  535. EXPECT_EQ(1, observer.enabled_count_);
  536. EXPECT_EQ(0, observer.disabled_by_user_count_);
  537. EXPECT_EQ(0, observer.disabled_by_session_abort_);
  538. EXPECT_EQ(0, observer.disabled_by_session_complete_);
  539. // Abort metalayer session.
  540. controller_->AbortSession();
  541. EXPECT_EQ(1, observer.enabled_count_);
  542. EXPECT_EQ(0, observer.disabled_by_user_count_);
  543. EXPECT_EQ(1, observer.disabled_by_session_abort_);
  544. EXPECT_EQ(0, observer.disabled_by_session_complete_);
  545. // Assert no-op when aborting an aborted session.
  546. controller_->AbortSession();
  547. EXPECT_EQ(1, observer.enabled_count_);
  548. EXPECT_EQ(0, observer.disabled_by_user_count_);
  549. EXPECT_EQ(1, observer.disabled_by_session_abort_);
  550. EXPECT_EQ(0, observer.disabled_by_session_complete_);
  551. // Assert no-op when aborting a completed session.
  552. tool_->OnEnable();
  553. EXPECT_EQ(2, observer.enabled_count_);
  554. EXPECT_EQ(0, observer.disabled_by_user_count_);
  555. EXPECT_EQ(1, observer.disabled_by_session_abort_);
  556. EXPECT_EQ(0, observer.disabled_by_session_complete_);
  557. controller_->UpdateEnabledState(
  558. HighlighterEnabledState::kDisabledBySessionComplete);
  559. EXPECT_EQ(2, observer.enabled_count_);
  560. EXPECT_EQ(0, observer.disabled_by_user_count_);
  561. EXPECT_EQ(1, observer.disabled_by_session_abort_);
  562. EXPECT_EQ(1, observer.disabled_by_session_complete_);
  563. controller_->AbortSession();
  564. EXPECT_EQ(2, observer.enabled_count_);
  565. EXPECT_EQ(0, observer.disabled_by_user_count_);
  566. EXPECT_EQ(1, observer.disabled_by_session_abort_);
  567. EXPECT_EQ(1, observer.disabled_by_session_complete_);
  568. // Assert no-op when aborting a disabled session.
  569. tool_->OnEnable();
  570. EXPECT_EQ(3, observer.enabled_count_);
  571. EXPECT_EQ(0, observer.disabled_by_user_count_);
  572. EXPECT_EQ(1, observer.disabled_by_session_abort_);
  573. EXPECT_EQ(1, observer.disabled_by_session_complete_);
  574. tool_->OnDisable();
  575. EXPECT_EQ(3, observer.enabled_count_);
  576. EXPECT_EQ(1, observer.disabled_by_user_count_);
  577. EXPECT_EQ(1, observer.disabled_by_session_abort_);
  578. EXPECT_EQ(1, observer.disabled_by_session_complete_);
  579. controller_->AbortSession();
  580. EXPECT_EQ(3, observer.enabled_count_);
  581. EXPECT_EQ(1, observer.disabled_by_user_count_);
  582. EXPECT_EQ(1, observer.disabled_by_session_abort_);
  583. EXPECT_EQ(1, observer.disabled_by_session_complete_);
  584. }
  585. } // namespace ash