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