| // Copyright 2017 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 "ash/highlighter/highlighter_controller.h" |
| |
| #include <memory> |
| |
| #include "ash/components/fast_ink/fast_ink_points.h" |
| #include "ash/highlighter/highlighter_controller_test_api.h" |
| #include "ash/shell.h" |
| #include "ash/system/palette/mock_palette_tool_delegate.h" |
| #include "ash/system/palette/palette_tool.h" |
| #include "ash/system/palette/tools/metalayer_mode.h" |
| #include "ash/test/ash_test_base.h" |
| #include "base/strings/stringprintf.h" |
| #include "ui/aura/window_tree_host.h" |
| #include "ui/compositor/test/draw_waiter_for_test.h" |
| #include "ui/events/test/event_generator.h" |
| |
| namespace ash { |
| namespace { |
| |
| class TestHighlighterObserver : public HighlighterController::Observer { |
| public: |
| TestHighlighterObserver() = default; |
| ~TestHighlighterObserver() override = default; |
| |
| // HighlighterController::Observer: |
| void OnHighlighterEnabledChanged(HighlighterEnabledState state) override { |
| switch (state) { |
| case HighlighterEnabledState::kEnabled: |
| ++enabled_count_; |
| break; |
| case HighlighterEnabledState::kDisabledByUser: |
| ++disabled_by_user_count_; |
| break; |
| case HighlighterEnabledState::kDisabledBySessionAbort: |
| ++disabled_by_session_abort_; |
| break; |
| case HighlighterEnabledState::kDisabledBySessionComplete: |
| ++disabled_by_session_complete_; |
| break; |
| } |
| } |
| |
| void OnHighlighterSelectionRecognized(const gfx::Rect& rect) override { |
| last_recognized_rect_ = rect; |
| } |
| |
| int enabled_count_ = 0; |
| int disabled_by_user_count_ = 0; |
| int disabled_by_session_abort_ = 0; |
| int disabled_by_session_complete_ = 0; |
| gfx::Rect last_recognized_rect_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(TestHighlighterObserver); |
| }; |
| |
| class HighlighterControllerTest : public AshTestBase { |
| public: |
| HighlighterControllerTest() = default; |
| ~HighlighterControllerTest() override = default; |
| |
| void SetUp() override { |
| AshTestBase::SetUp(); |
| controller_ = Shell::Get()->highlighter_controller(); |
| controller_test_api_ = |
| std::make_unique<HighlighterControllerTestApi>(controller_); |
| |
| palette_tool_delegate_ = std::make_unique<MockPaletteToolDelegate>(); |
| tool_ = std::make_unique<MetalayerMode>(palette_tool_delegate_.get()); |
| } |
| |
| void TearDown() override { |
| tool_.reset(); |
| // This needs to be called first to reset the controller state before the |
| // shell instance gets torn down. |
| controller_test_api_.reset(); |
| AshTestBase::TearDown(); |
| } |
| |
| void UpdateDisplayAndWaitForCompositingEnded( |
| const std::string& display_specs) { |
| UpdateDisplay(display_specs); |
| ui::DrawWaiterForTest::WaitForCompositingEnded( |
| Shell::GetPrimaryRootWindow()->GetHost()->compositor()); |
| } |
| |
| protected: |
| void TraceRect(const gfx::Rect& rect) { |
| ui::test::EventGenerator* event_generator = GetEventGenerator(); |
| event_generator->MoveTouch(gfx::Point(rect.x(), rect.y())); |
| event_generator->PressTouch(); |
| event_generator->MoveTouch(gfx::Point(rect.right(), rect.y())); |
| event_generator->MoveTouch(gfx::Point(rect.right(), rect.bottom())); |
| event_generator->MoveTouch(gfx::Point(rect.x(), rect.bottom())); |
| event_generator->MoveTouch(gfx::Point(rect.x(), rect.y())); |
| event_generator->ReleaseTouch(); |
| |
| // The the events above will trigger a frame, so wait until a new |
| // CompositorFrame is generated before terminating. |
| ui::DrawWaiterForTest::WaitForCompositingEnded( |
| Shell::GetPrimaryRootWindow()->GetHost()->compositor()); |
| } |
| |
| std::unique_ptr<HighlighterControllerTestApi> controller_test_api_; |
| std::unique_ptr<MockPaletteToolDelegate> palette_tool_delegate_; |
| std::unique_ptr<PaletteTool> tool_; |
| |
| HighlighterController* controller_ = nullptr; // Not owned. |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(HighlighterControllerTest); |
| }; |
| |
| } // namespace |
| |
| // Test to ensure the class responsible for drawing the highlighter pointer |
| // receives points from stylus movements as expected. |
| TEST_F(HighlighterControllerTest, HighlighterRenderer) { |
| // The highlighter pointer mode only works with stylus. |
| ui::test::EventGenerator* event_generator = GetEventGenerator(); |
| event_generator->EnterPenPointerMode(); |
| |
| // When disabled the highlighter pointer should not be showing. |
| event_generator->MoveTouch(gfx::Point(1, 1)); |
| EXPECT_FALSE(controller_test_api_->IsShowingHighlighter()); |
| |
| // Verify that by enabling the mode, the highlighter pointer should still not |
| // be showing. |
| controller_test_api_->SetEnabled(true); |
| EXPECT_FALSE(controller_test_api_->IsShowingHighlighter()); |
| |
| // Verify moving the stylus 4 times will not display the highlighter pointer. |
| event_generator->MoveTouch(gfx::Point(2, 2)); |
| event_generator->MoveTouch(gfx::Point(3, 3)); |
| event_generator->MoveTouch(gfx::Point(4, 4)); |
| event_generator->MoveTouch(gfx::Point(5, 5)); |
| EXPECT_FALSE(controller_test_api_->IsShowingHighlighter()); |
| |
| // Verify pressing the stylus will show the highlighter pointer and add a |
| // point but will not activate fading out. |
| event_generator->PressTouch(); |
| EXPECT_TRUE(controller_test_api_->IsShowingHighlighter()); |
| EXPECT_FALSE(controller_test_api_->IsFadingAway()); |
| EXPECT_EQ(1, controller_test_api_->points().GetNumberOfPoints()); |
| |
| // Verify dragging the stylus 2 times will add 2 more points. |
| event_generator->MoveTouch(gfx::Point(6, 6)); |
| event_generator->MoveTouch(gfx::Point(7, 7)); |
| EXPECT_EQ(3, controller_test_api_->points().GetNumberOfPoints()); |
| |
| // Verify releasing the stylus still shows the highlighter pointer, which is |
| // fading away. |
| event_generator->ReleaseTouch(); |
| EXPECT_TRUE(controller_test_api_->IsShowingHighlighter()); |
| EXPECT_TRUE(controller_test_api_->IsFadingAway()); |
| |
| // Verify that disabling the mode right after the gesture completion does not |
| // hide the highlighter pointer immediately but lets it play out the |
| // animation. |
| controller_test_api_->SetEnabled(false); |
| EXPECT_TRUE(controller_test_api_->IsShowingHighlighter()); |
| EXPECT_TRUE(controller_test_api_->IsFadingAway()); |
| |
| // Verify that disabling the mode mid-gesture hides the highlighter pointer |
| // immediately. |
| controller_test_api_->DestroyPointerView(); |
| controller_test_api_->SetEnabled(true); |
| event_generator->PressTouch(); |
| event_generator->MoveTouch(gfx::Point(6, 6)); |
| EXPECT_TRUE(controller_test_api_->IsShowingHighlighter()); |
| controller_test_api_->SetEnabled(false); |
| EXPECT_FALSE(controller_test_api_->IsShowingHighlighter()); |
| |
| // Verify that the highlighter pointer does not add points while disabled. |
| event_generator->PressTouch(); |
| event_generator->MoveTouch(gfx::Point(8, 8)); |
| event_generator->ReleaseTouch(); |
| event_generator->MoveTouch(gfx::Point(9, 9)); |
| EXPECT_FALSE(controller_test_api_->IsShowingHighlighter()); |
| |
| // Verify that the highlighter pointer does not get shown if points are not |
| // coming from the stylus, even when enabled. |
| event_generator->ExitPenPointerMode(); |
| controller_test_api_->SetEnabled(true); |
| event_generator->PressTouch(); |
| event_generator->MoveTouch(gfx::Point(10, 10)); |
| event_generator->MoveTouch(gfx::Point(11, 11)); |
| EXPECT_FALSE(controller_test_api_->IsShowingHighlighter()); |
| event_generator->ReleaseTouch(); |
| } |
| |
| // Test to ensure the class responsible for drawing the highlighter pointer |
| // handles prediction as expected when it receives points from stylus movements. |
| TEST_F(HighlighterControllerTest, HighlighterPrediction) { |
| controller_test_api_->SetEnabled(true); |
| // The highlighter pointer mode only works with stylus. |
| ui::test::EventGenerator* event_generator = GetEventGenerator(); |
| event_generator->EnterPenPointerMode(); |
| event_generator->PressTouch(); |
| EXPECT_TRUE(controller_test_api_->IsShowingHighlighter()); |
| |
| EXPECT_EQ(1, controller_test_api_->points().GetNumberOfPoints()); |
| // Initial press event shouldn't generate any predicted points as there's no |
| // history to use for prediction. |
| EXPECT_EQ(0, controller_test_api_->predicted_points().GetNumberOfPoints()); |
| |
| // Verify dragging the stylus 3 times will add some predicted points. |
| event_generator->MoveTouch(gfx::Point(10, 10)); |
| event_generator->MoveTouch(gfx::Point(20, 20)); |
| event_generator->MoveTouch(gfx::Point(30, 30)); |
| EXPECT_NE(0, controller_test_api_->predicted_points().GetNumberOfPoints()); |
| // Verify predicted points are in the right direction. |
| for (const auto& point : controller_test_api_->predicted_points().points()) { |
| EXPECT_LT(30, point.location.x()); |
| EXPECT_LT(30, point.location.y()); |
| } |
| } |
| |
| // Test that stylus gestures are correctly recognized by HighlighterController. |
| TEST_F(HighlighterControllerTest, HighlighterGestures) { |
| controller_test_api_->SetEnabled(true); |
| ui::test::EventGenerator* event_generator = GetEventGenerator(); |
| event_generator->EnterPenPointerMode(); |
| |
| TestHighlighterObserver observer; |
| controller_->AddObserver(&observer); |
| |
| // A non-horizontal stroke is not recognized |
| controller_test_api_->ResetSelection(); |
| event_generator->MoveTouch(gfx::Point(100, 100)); |
| event_generator->PressTouch(); |
| event_generator->MoveTouch(gfx::Point(200, 200)); |
| event_generator->ReleaseTouch(); |
| EXPECT_FALSE(controller_test_api_->HandleSelectionCalled()); |
| |
| // An almost horizontal stroke is recognized |
| controller_test_api_->ResetSelection(); |
| event_generator->MoveTouch(gfx::Point(100, 100)); |
| event_generator->PressTouch(); |
| event_generator->MoveTouch(gfx::Point(300, 102)); |
| event_generator->ReleaseTouch(); |
| EXPECT_TRUE(controller_test_api_->HandleSelectionCalled()); |
| |
| // Horizontal stroke selection rectangle should: |
| // have the same horizontal center line as the stroke bounding box, |
| // be 4dp wider than the stroke bounding box, |
| // be exactly 14dp high. |
| gfx::Rect expected_rect(98, 94, 204, 14); |
| EXPECT_EQ(expected_rect, controller_test_api_->selection()); |
| EXPECT_EQ(expected_rect, observer.last_recognized_rect_); |
| |
| // An insufficiently closed C-like shape is not recognized |
| controller_test_api_->ResetSelection(); |
| event_generator->MoveTouch(gfx::Point(100, 0)); |
| event_generator->PressTouch(); |
| event_generator->MoveTouch(gfx::Point(0, 0)); |
| event_generator->MoveTouch(gfx::Point(0, 100)); |
| event_generator->MoveTouch(gfx::Point(100, 100)); |
| event_generator->ReleaseTouch(); |
| EXPECT_FALSE(controller_test_api_->HandleSelectionCalled()); |
| |
| // An almost closed G-like shape is recognized |
| controller_test_api_->ResetSelection(); |
| event_generator->MoveTouch(gfx::Point(200, 0)); |
| event_generator->PressTouch(); |
| event_generator->MoveTouch(gfx::Point(0, 0)); |
| event_generator->MoveTouch(gfx::Point(0, 100)); |
| event_generator->MoveTouch(gfx::Point(200, 100)); |
| event_generator->MoveTouch(gfx::Point(200, 20)); |
| event_generator->ReleaseTouch(); |
| EXPECT_TRUE(controller_test_api_->HandleSelectionCalled()); |
| expected_rect = gfx::Rect(0, 0, 200, 100); |
| EXPECT_EQ(expected_rect, controller_test_api_->selection()); |
| EXPECT_EQ(expected_rect, observer.last_recognized_rect_); |
| |
| // A closed diamond shape is recognized |
| controller_test_api_->ResetSelection(); |
| event_generator->MoveTouch(gfx::Point(100, 50)); |
| event_generator->PressTouch(); |
| event_generator->MoveTouch(gfx::Point(200, 150)); |
| event_generator->MoveTouch(gfx::Point(100, 250)); |
| event_generator->MoveTouch(gfx::Point(0, 150)); |
| event_generator->MoveTouch(gfx::Point(100, 50)); |
| event_generator->ReleaseTouch(); |
| EXPECT_TRUE(controller_test_api_->HandleSelectionCalled()); |
| expected_rect = gfx::Rect(0, 50, 200, 200); |
| EXPECT_EQ(expected_rect, controller_test_api_->selection()); |
| EXPECT_EQ(expected_rect, observer.last_recognized_rect_); |
| |
| controller_->RemoveObserver(&observer); |
| } |
| |
| // Disabled due to https://crbug.com/917113. |
| TEST_F(HighlighterControllerTest, DISABLED_HighlighterGesturesScaled) { |
| controller_test_api_->SetEnabled(true); |
| ui::test::EventGenerator* event_generator = GetEventGenerator(); |
| event_generator->EnterPenPointerMode(); |
| |
| const gfx::Rect original_rect(200, 100, 400, 300); |
| |
| // Allow for rounding errors. |
| gfx::Rect inflated(original_rect); |
| inflated.Inset(-1, -1); |
| |
| constexpr float display_scales[] = {1.f, 1.5f, 2.0f}; |
| constexpr float ui_scales[] = {0.5f, 0.67f, 1.0f, 1.25f, |
| 1.33f, 1.5f, 1.67f, 2.0f}; |
| |
| for (size_t i = 0; i < sizeof(display_scales) / sizeof(float); ++i) { |
| const float display_scale = display_scales[i]; |
| for (size_t j = 0; j < sizeof(ui_scales) / sizeof(float); ++j) { |
| const float ui_scale = ui_scales[j]; |
| |
| std::string display_spec = |
| base::StringPrintf("1500x1000*%.2f@%.2f", display_scale, ui_scale); |
| SCOPED_TRACE(display_spec); |
| UpdateDisplayAndWaitForCompositingEnded(display_spec); |
| |
| controller_test_api_->ResetSelection(); |
| TraceRect(original_rect); |
| EXPECT_TRUE(controller_test_api_->HandleSelectionCalled()); |
| |
| const gfx::Rect selection = controller_test_api_->selection(); |
| EXPECT_TRUE(inflated.Contains(selection)); |
| EXPECT_TRUE(selection.Contains(original_rect)); |
| } |
| } |
| } |
| |
| // Test that stylus gesture recognition correctly handles display rotation |
| TEST_F(HighlighterControllerTest, HighlighterGesturesRotated) { |
| controller_test_api_->SetEnabled(true); |
| ui::test::EventGenerator* event_generator = GetEventGenerator(); |
| event_generator->EnterPenPointerMode(); |
| |
| const gfx::Rect trace(200, 100, 400, 300); |
| |
| // No rotation |
| UpdateDisplayAndWaitForCompositingEnded("1500x1000"); |
| controller_test_api_->ResetSelection(); |
| TraceRect(trace); |
| EXPECT_TRUE(controller_test_api_->HandleSelectionCalled()); |
| EXPECT_EQ("200,100 400x300", controller_test_api_->selection().ToString()); |
| |
| // Rotate to 90 degrees |
| UpdateDisplayAndWaitForCompositingEnded("1500x1000/r"); |
| controller_test_api_->ResetSelection(); |
| TraceRect(trace); |
| EXPECT_TRUE(controller_test_api_->HandleSelectionCalled()); |
| EXPECT_EQ("100,900 300x400", controller_test_api_->selection().ToString()); |
| |
| // Rotate to 180 degrees |
| UpdateDisplayAndWaitForCompositingEnded("1500x1000/u"); |
| controller_test_api_->ResetSelection(); |
| TraceRect(trace); |
| EXPECT_TRUE(controller_test_api_->HandleSelectionCalled()); |
| EXPECT_EQ("900,600 400x300", controller_test_api_->selection().ToString()); |
| |
| // Rotate to 270 degrees |
| UpdateDisplayAndWaitForCompositingEnded("1500x1000/l"); |
| controller_test_api_->ResetSelection(); |
| TraceRect(trace); |
| EXPECT_TRUE(controller_test_api_->HandleSelectionCalled()); |
| EXPECT_EQ("600,200 300x400", controller_test_api_->selection().ToString()); |
| } |
| |
| // Test that a stroke interrupted close to the screen edge is treated as |
| // contiguous. |
| TEST_F(HighlighterControllerTest, InterruptedStroke) { |
| controller_test_api_->SetEnabled(true); |
| ui::test::EventGenerator* event_generator = GetEventGenerator(); |
| event_generator->EnterPenPointerMode(); |
| |
| UpdateDisplayAndWaitForCompositingEnded("1500x1000"); |
| |
| // An interrupted stroke close to the screen edge should be recognized as a |
| // contiguous stroke. |
| controller_test_api_->ResetSelection(); |
| event_generator->MoveTouch(gfx::Point(300, 100)); |
| event_generator->PressTouch(); |
| event_generator->MoveTouch(gfx::Point(0, 100)); |
| event_generator->ReleaseTouch(); |
| EXPECT_TRUE(controller_test_api_->IsWaitingToResumeStroke()); |
| EXPECT_FALSE(controller_test_api_->HandleSelectionCalled()); |
| EXPECT_FALSE(controller_test_api_->IsFadingAway()); |
| |
| event_generator->MoveTouch(gfx::Point(0, 200)); |
| event_generator->PressTouch(); |
| event_generator->MoveTouch(gfx::Point(300, 200)); |
| event_generator->ReleaseTouch(); |
| EXPECT_FALSE(controller_test_api_->IsWaitingToResumeStroke()); |
| EXPECT_TRUE(controller_test_api_->HandleSelectionCalled()); |
| EXPECT_EQ("0,100 300x100", controller_test_api_->selection().ToString()); |
| |
| // Repeat the same gesture, but simulate a timeout after the gap. This should |
| // force the gesture completion. |
| controller_test_api_->ResetSelection(); |
| event_generator->MoveTouch(gfx::Point(300, 100)); |
| event_generator->PressTouch(); |
| event_generator->MoveTouch(gfx::Point(0, 100)); |
| event_generator->ReleaseTouch(); |
| EXPECT_TRUE(controller_test_api_->IsWaitingToResumeStroke()); |
| EXPECT_FALSE(controller_test_api_->HandleSelectionCalled()); |
| EXPECT_FALSE(controller_test_api_->IsFadingAway()); |
| |
| controller_test_api_->SimulateInterruptedStrokeTimeout(); |
| EXPECT_FALSE(controller_test_api_->IsWaitingToResumeStroke()); |
| EXPECT_TRUE(controller_test_api_->HandleSelectionCalled()); |
| EXPECT_TRUE(controller_test_api_->IsFadingAway()); |
| } |
| |
| // Test that the selection is never crossing the screen bounds. |
| // |
| // Disabled due to https://crbug.com/917113. |
| TEST_F(HighlighterControllerTest, DISABLED_SelectionInsideScreen) { |
| controller_test_api_->SetEnabled(true); |
| ui::test::EventGenerator* event_generator = GetEventGenerator(); |
| event_generator->EnterPenPointerMode(); |
| |
| constexpr float display_scales[] = {1.f, 1.5f, 2.0f}; |
| |
| for (size_t i = 0; i < sizeof(display_scales) / sizeof(float); ++i) { |
| // 2nd display is for offscreen test. |
| std::string display_spec = base::StringPrintf( |
| "1000x1000*%.2f,500x1000*%.2f", display_scales[i], display_scales[i]); |
| SCOPED_TRACE(display_spec); |
| UpdateDisplayAndWaitForCompositingEnded(display_spec); |
| |
| const gfx::Rect screen(0, 0, 1000, 1000); |
| |
| // Rectangle completely offscreen. |
| controller_test_api_->ResetSelection(); |
| TraceRect(gfx::Rect(-100, -100, 10, 10)); |
| controller_test_api_->SimulateInterruptedStrokeTimeout(); |
| EXPECT_FALSE(controller_test_api_->HandleSelectionCalled()); |
| |
| // Rectangle crossing the left edge. |
| controller_test_api_->ResetSelection(); |
| TraceRect(gfx::Rect(-100, 100, 200, 200)); |
| controller_test_api_->SimulateInterruptedStrokeTimeout(); |
| EXPECT_TRUE(controller_test_api_->HandleSelectionCalled()); |
| EXPECT_TRUE(screen.Contains(controller_test_api_->selection())); |
| |
| // Rectangle crossing the top edge. |
| controller_test_api_->ResetSelection(); |
| TraceRect(gfx::Rect(100, -100, 200, 200)); |
| controller_test_api_->SimulateInterruptedStrokeTimeout(); |
| EXPECT_TRUE(controller_test_api_->HandleSelectionCalled()); |
| EXPECT_TRUE(screen.Contains(controller_test_api_->selection())); |
| |
| // Rectangle crossing the right edge. |
| controller_test_api_->ResetSelection(); |
| TraceRect(gfx::Rect(900, 100, 200, 200)); |
| controller_test_api_->SimulateInterruptedStrokeTimeout(); |
| EXPECT_TRUE(controller_test_api_->HandleSelectionCalled()); |
| EXPECT_TRUE(screen.Contains(controller_test_api_->selection())); |
| |
| // Rectangle crossing the bottom edge. |
| controller_test_api_->ResetSelection(); |
| TraceRect(gfx::Rect(100, 900, 200, 200)); |
| controller_test_api_->SimulateInterruptedStrokeTimeout(); |
| EXPECT_TRUE(controller_test_api_->HandleSelectionCalled()); |
| EXPECT_TRUE(screen.Contains(controller_test_api_->selection())); |
| |
| // Vertical stroke completely offscreen. |
| controller_test_api_->ResetSelection(); |
| event_generator->MoveTouch(gfx::Point(1100, 100)); |
| event_generator->PressTouch(); |
| event_generator->MoveTouch(gfx::Point(1100, 500)); |
| event_generator->ReleaseTouch(); |
| controller_test_api_->SimulateInterruptedStrokeTimeout(); |
| EXPECT_FALSE(controller_test_api_->HandleSelectionCalled()); |
| |
| // Horizontal stroke along the top edge of the screen. |
| controller_test_api_->ResetSelection(); |
| event_generator->MoveTouch(gfx::Point(0, 0)); |
| event_generator->PressTouch(); |
| event_generator->MoveTouch(gfx::Point(1000, 0)); |
| event_generator->ReleaseTouch(); |
| controller_test_api_->SimulateInterruptedStrokeTimeout(); |
| EXPECT_TRUE(controller_test_api_->HandleSelectionCalled()); |
| EXPECT_TRUE(screen.Contains(controller_test_api_->selection())); |
| |
| // Horizontal stroke along the bottom edge of the screen. |
| controller_test_api_->ResetSelection(); |
| event_generator->MoveTouch(gfx::Point(0, 999)); |
| event_generator->PressTouch(); |
| event_generator->MoveTouch(gfx::Point(1000, 999)); |
| event_generator->ReleaseTouch(); |
| controller_test_api_->SimulateInterruptedStrokeTimeout(); |
| EXPECT_TRUE(controller_test_api_->HandleSelectionCalled()); |
| EXPECT_TRUE(screen.Contains(controller_test_api_->selection())); |
| } |
| } |
| |
| // Test that a detached client does not receive notifications. |
| TEST_F(HighlighterControllerTest, DetachedClient) { |
| controller_test_api_->SetEnabled(true); |
| ui::test::EventGenerator* event_generator = GetEventGenerator(); |
| event_generator->EnterPenPointerMode(); |
| |
| UpdateDisplayAndWaitForCompositingEnded("1500x1000"); |
| const gfx::Rect trace(200, 100, 400, 300); |
| |
| // Detach the client, no notifications should reach it. |
| controller_test_api_->DetachClient(); |
| |
| controller_test_api_->ResetEnabledState(); |
| controller_test_api_->SetEnabled(false); |
| EXPECT_FALSE(controller_test_api_->HandleEnabledStateChangedCalled()); |
| controller_test_api_->SetEnabled(true); |
| EXPECT_FALSE(controller_test_api_->HandleEnabledStateChangedCalled()); |
| |
| controller_test_api_->ResetSelection(); |
| TraceRect(trace); |
| EXPECT_FALSE(controller_test_api_->HandleSelectionCalled()); |
| |
| // Attach the client again, notifications should be delivered normally. |
| controller_test_api_->AttachClient(); |
| |
| controller_test_api_->ResetEnabledState(); |
| controller_test_api_->SetEnabled(false); |
| EXPECT_TRUE(controller_test_api_->HandleEnabledStateChangedCalled()); |
| controller_test_api_->SetEnabled(true); |
| EXPECT_TRUE(controller_test_api_->HandleEnabledStateChangedCalled()); |
| |
| controller_test_api_->ResetSelection(); |
| TraceRect(trace); |
| EXPECT_TRUE(controller_test_api_->HandleSelectionCalled()); |
| } |
| |
| // Test enabling/disabling metalayer mode by selecting/deselecting on palette |
| // tool and calling UpdateEnabledState notify observers properly. |
| TEST_F(HighlighterControllerTest, UpdateEnabledState) { |
| TestHighlighterObserver observer; |
| controller_->AddObserver(&observer); |
| |
| // Assert initial state. |
| ASSERT_EQ(0, observer.enabled_count_); |
| ASSERT_EQ(0, observer.disabled_by_user_count_); |
| ASSERT_EQ(0, observer.disabled_by_session_abort_); |
| ASSERT_EQ(0, observer.disabled_by_session_complete_); |
| |
| // Test enabling. |
| tool_->OnEnable(); |
| EXPECT_EQ(1, observer.enabled_count_); |
| EXPECT_EQ(0, observer.disabled_by_user_count_); |
| EXPECT_EQ(0, observer.disabled_by_session_abort_); |
| EXPECT_EQ(0, observer.disabled_by_session_complete_); |
| |
| // Test disabling by user. |
| tool_->OnDisable(); |
| EXPECT_EQ(1, observer.enabled_count_); |
| EXPECT_EQ(1, observer.disabled_by_user_count_); |
| EXPECT_EQ(0, observer.disabled_by_session_abort_); |
| EXPECT_EQ(0, observer.disabled_by_session_complete_); |
| |
| // Test disabling by session abort. |
| tool_->OnEnable(); |
| EXPECT_EQ(2, observer.enabled_count_); |
| EXPECT_EQ(1, observer.disabled_by_user_count_); |
| EXPECT_EQ(0, observer.disabled_by_session_abort_); |
| EXPECT_EQ(0, observer.disabled_by_session_complete_); |
| controller_->UpdateEnabledState( |
| HighlighterEnabledState::kDisabledBySessionAbort); |
| EXPECT_EQ(2, observer.enabled_count_); |
| EXPECT_EQ(1, observer.disabled_by_user_count_); |
| EXPECT_EQ(1, observer.disabled_by_session_abort_); |
| EXPECT_EQ(0, observer.disabled_by_session_complete_); |
| |
| // Test disabling by session complete. |
| tool_->OnEnable(); |
| EXPECT_EQ(3, observer.enabled_count_); |
| EXPECT_EQ(1, observer.disabled_by_user_count_); |
| EXPECT_EQ(1, observer.disabled_by_session_abort_); |
| EXPECT_EQ(0, observer.disabled_by_session_complete_); |
| controller_->UpdateEnabledState( |
| HighlighterEnabledState::kDisabledBySessionComplete); |
| EXPECT_EQ(3, observer.enabled_count_); |
| EXPECT_EQ(1, observer.disabled_by_user_count_); |
| EXPECT_EQ(1, observer.disabled_by_session_abort_); |
| EXPECT_EQ(1, observer.disabled_by_session_complete_); |
| |
| controller_->RemoveObserver(&observer); |
| } |
| |
| // Test aborting a metalayer session and notifying observers properly. |
| TEST_F(HighlighterControllerTest, AbortSession) { |
| TestHighlighterObserver observer; |
| controller_->AddObserver(&observer); |
| |
| // Assert initial state. |
| ASSERT_EQ(0, observer.enabled_count_); |
| ASSERT_EQ(0, observer.disabled_by_user_count_); |
| ASSERT_EQ(0, observer.disabled_by_session_abort_); |
| ASSERT_EQ(0, observer.disabled_by_session_complete_); |
| |
| // Start metalayer session. |
| tool_->OnEnable(); |
| EXPECT_EQ(1, observer.enabled_count_); |
| EXPECT_EQ(0, observer.disabled_by_user_count_); |
| EXPECT_EQ(0, observer.disabled_by_session_abort_); |
| EXPECT_EQ(0, observer.disabled_by_session_complete_); |
| |
| // Abort metalayer session. |
| controller_->AbortSession(); |
| EXPECT_EQ(1, observer.enabled_count_); |
| EXPECT_EQ(0, observer.disabled_by_user_count_); |
| EXPECT_EQ(1, observer.disabled_by_session_abort_); |
| EXPECT_EQ(0, observer.disabled_by_session_complete_); |
| |
| // Assert no-op when aborting an aborted session. |
| controller_->AbortSession(); |
| EXPECT_EQ(1, observer.enabled_count_); |
| EXPECT_EQ(0, observer.disabled_by_user_count_); |
| EXPECT_EQ(1, observer.disabled_by_session_abort_); |
| EXPECT_EQ(0, observer.disabled_by_session_complete_); |
| |
| // Assert no-op when aborting a completed session. |
| tool_->OnEnable(); |
| EXPECT_EQ(2, observer.enabled_count_); |
| EXPECT_EQ(0, observer.disabled_by_user_count_); |
| EXPECT_EQ(1, observer.disabled_by_session_abort_); |
| EXPECT_EQ(0, observer.disabled_by_session_complete_); |
| controller_->UpdateEnabledState( |
| HighlighterEnabledState::kDisabledBySessionComplete); |
| EXPECT_EQ(2, observer.enabled_count_); |
| EXPECT_EQ(0, observer.disabled_by_user_count_); |
| EXPECT_EQ(1, observer.disabled_by_session_abort_); |
| EXPECT_EQ(1, observer.disabled_by_session_complete_); |
| controller_->AbortSession(); |
| EXPECT_EQ(2, observer.enabled_count_); |
| EXPECT_EQ(0, observer.disabled_by_user_count_); |
| EXPECT_EQ(1, observer.disabled_by_session_abort_); |
| EXPECT_EQ(1, observer.disabled_by_session_complete_); |
| |
| // Assert no-op when aborting a disabled session. |
| tool_->OnEnable(); |
| EXPECT_EQ(3, observer.enabled_count_); |
| EXPECT_EQ(0, observer.disabled_by_user_count_); |
| EXPECT_EQ(1, observer.disabled_by_session_abort_); |
| EXPECT_EQ(1, observer.disabled_by_session_complete_); |
| tool_->OnDisable(); |
| EXPECT_EQ(3, observer.enabled_count_); |
| EXPECT_EQ(1, observer.disabled_by_user_count_); |
| EXPECT_EQ(1, observer.disabled_by_session_abort_); |
| EXPECT_EQ(1, observer.disabled_by_session_complete_); |
| controller_->AbortSession(); |
| EXPECT_EQ(3, observer.enabled_count_); |
| EXPECT_EQ(1, observer.disabled_by_user_count_); |
| EXPECT_EQ(1, observer.disabled_by_session_abort_); |
| EXPECT_EQ(1, observer.disabled_by_session_complete_); |
| } |
| |
| } // namespace ash |