| // Copyright 2015 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 "ui/views/animation/ink_drop_highlight.h" |
| |
| #include <memory> |
| |
| #include "base/macros.h" |
| #include "base/time/time.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/compositor/scoped_animation_duration_scale_mode.h" |
| #include "ui/gfx/animation/animation.h" |
| #include "ui/gfx/animation/animation_test_api.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/transform.h" |
| #include "ui/views/animation/test/ink_drop_highlight_test_api.h" |
| #include "ui/views/animation/test/test_ink_drop_highlight_observer.h" |
| |
| namespace views { |
| namespace test { |
| |
| class InkDropHighlightTest : public testing::Test { |
| public: |
| InkDropHighlightTest(); |
| ~InkDropHighlightTest() override; |
| |
| protected: |
| InkDropHighlight* ink_drop_highlight() { return ink_drop_highlight_.get(); } |
| |
| InkDropHighlightTestApi* test_api() { return test_api_.get(); } |
| |
| // Observer of the test target. |
| TestInkDropHighlightObserver* observer() { return &observer_; } |
| |
| // Initializes |ink_drop_highlight_| and attaches |test_api_| and |observer_| |
| // to the new instance. |
| void InitHighlight(std::unique_ptr<InkDropHighlight> new_highlight); |
| |
| // Destroys the |ink_drop_highlight_| and the attached |test_api_|. |
| void DestroyHighlight(); |
| |
| private: |
| // The test target. |
| std::unique_ptr<InkDropHighlight> ink_drop_highlight_; |
| |
| // Allows privileged access to the the |ink_drop_highlight_|. |
| std::unique_ptr<InkDropHighlightTestApi> test_api_; |
| |
| // Observer of the test target. |
| TestInkDropHighlightObserver observer_; |
| |
| std::unique_ptr<base::AutoReset<gfx::Animation::RichAnimationRenderMode>> |
| animation_mode_reset_; |
| |
| DISALLOW_COPY_AND_ASSIGN(InkDropHighlightTest); |
| }; |
| |
| InkDropHighlightTest::InkDropHighlightTest() |
| : animation_mode_reset_(gfx::AnimationTestApi::SetRichAnimationRenderMode( |
| gfx::Animation::RichAnimationRenderMode::FORCE_DISABLED)) { |
| InitHighlight(std::make_unique<InkDropHighlight>( |
| gfx::Size(10, 10), 3, gfx::PointF(), SK_ColorBLACK)); |
| } |
| |
| InkDropHighlightTest::~InkDropHighlightTest() { |
| // Destory highlight to make sure it is destroyed before the observer. |
| DestroyHighlight(); |
| } |
| |
| void InkDropHighlightTest::InitHighlight( |
| std::unique_ptr<InkDropHighlight> new_highlight) { |
| ink_drop_highlight_ = std::move(new_highlight); |
| test_api_ = |
| std::make_unique<InkDropHighlightTestApi>(ink_drop_highlight_.get()); |
| test_api()->SetDisableAnimationTimers(true); |
| ink_drop_highlight()->set_observer(&observer_); |
| } |
| |
| void InkDropHighlightTest::DestroyHighlight() { |
| test_api_.reset(); |
| ink_drop_highlight_.reset(); |
| } |
| |
| TEST_F(InkDropHighlightTest, InitialStateAfterConstruction) { |
| EXPECT_FALSE(ink_drop_highlight()->IsFadingInOrVisible()); |
| } |
| |
| TEST_F(InkDropHighlightTest, IsHighlightedStateTransitions) { |
| ink_drop_highlight()->FadeIn(base::TimeDelta::FromSeconds(1)); |
| EXPECT_TRUE(ink_drop_highlight()->IsFadingInOrVisible()); |
| |
| test_api()->CompleteAnimations(); |
| EXPECT_TRUE(ink_drop_highlight()->IsFadingInOrVisible()); |
| |
| ink_drop_highlight()->FadeOut(base::TimeDelta::FromSeconds(1), |
| false /* explode */); |
| EXPECT_FALSE(ink_drop_highlight()->IsFadingInOrVisible()); |
| |
| test_api()->CompleteAnimations(); |
| EXPECT_FALSE(ink_drop_highlight()->IsFadingInOrVisible()); |
| } |
| |
| TEST_F(InkDropHighlightTest, VerifyObserversAreNotified) { |
| // TODO(bruthig): Re-enable! For some reason these tests fail on some win |
| // trunk builds. See crbug.com/731811. |
| if (!gfx::Animation::ShouldRenderRichAnimation()) |
| return; |
| |
| ink_drop_highlight()->FadeIn(base::TimeDelta::FromSeconds(1)); |
| |
| EXPECT_EQ(1, observer()->last_animation_started_ordinal()); |
| EXPECT_FALSE(observer()->AnimationHasEnded()); |
| |
| test_api()->CompleteAnimations(); |
| |
| EXPECT_TRUE(observer()->AnimationHasEnded()); |
| EXPECT_EQ(2, observer()->last_animation_ended_ordinal()); |
| } |
| |
| TEST_F(InkDropHighlightTest, |
| VerifyObserversAreNotifiedWithCorrectAnimationType) { |
| ink_drop_highlight()->FadeIn(base::TimeDelta::FromSeconds(1)); |
| |
| EXPECT_TRUE(observer()->AnimationHasStarted()); |
| EXPECT_EQ(InkDropHighlight::FADE_IN, |
| observer()->last_animation_started_context()); |
| |
| test_api()->CompleteAnimations(); |
| EXPECT_TRUE(observer()->AnimationHasEnded()); |
| EXPECT_EQ(InkDropHighlight::FADE_IN, |
| observer()->last_animation_started_context()); |
| |
| ink_drop_highlight()->FadeOut(base::TimeDelta::FromSeconds(1), |
| false /* explode */); |
| EXPECT_EQ(InkDropHighlight::FADE_OUT, |
| observer()->last_animation_started_context()); |
| |
| test_api()->CompleteAnimations(); |
| EXPECT_EQ(InkDropHighlight::FADE_OUT, |
| observer()->last_animation_started_context()); |
| } |
| |
| TEST_F(InkDropHighlightTest, VerifyObserversAreNotifiedOfSuccessfulAnimations) { |
| ink_drop_highlight()->FadeIn(base::TimeDelta::FromSeconds(1)); |
| test_api()->CompleteAnimations(); |
| |
| EXPECT_EQ(2, observer()->last_animation_ended_ordinal()); |
| EXPECT_EQ(InkDropAnimationEndedReason::SUCCESS, |
| observer()->last_animation_ended_reason()); |
| } |
| |
| TEST_F(InkDropHighlightTest, VerifyObserversAreNotifiedOfPreemptedAnimations) { |
| // TODO(bruthig): Re-enable! For some reason these tests fail on some win |
| // trunk builds. See crbug.com/731811. |
| if (!gfx::Animation::ShouldRenderRichAnimation()) |
| return; |
| |
| ink_drop_highlight()->FadeIn(base::TimeDelta::FromSeconds(1)); |
| ink_drop_highlight()->FadeOut(base::TimeDelta::FromSeconds(1), |
| false /* explode */); |
| |
| EXPECT_EQ(2, observer()->last_animation_ended_ordinal()); |
| EXPECT_EQ(InkDropHighlight::FADE_IN, |
| observer()->last_animation_ended_context()); |
| EXPECT_EQ(InkDropAnimationEndedReason::PRE_EMPTED, |
| observer()->last_animation_ended_reason()); |
| } |
| |
| // Confirms there is no crash. |
| TEST_F(InkDropHighlightTest, NullObserverIsSafe) { |
| ink_drop_highlight()->set_observer(nullptr); |
| |
| ink_drop_highlight()->FadeIn(base::TimeDelta::FromSeconds(1)); |
| test_api()->CompleteAnimations(); |
| |
| ink_drop_highlight()->FadeOut(base::TimeDelta::FromMilliseconds(0), |
| false /* explode */); |
| test_api()->CompleteAnimations(); |
| EXPECT_FALSE(ink_drop_highlight()->IsFadingInOrVisible()); |
| } |
| |
| // Verify animations are aborted during deletion and the |
| // InkDropHighlightObservers are notified. |
| TEST_F(InkDropHighlightTest, AnimationsAbortedDuringDeletion) { |
| // TODO(bruthig): Re-enable! For some reason these tests fail on some win |
| // trunk builds. See crbug.com/731811. |
| if (!gfx::Animation::ShouldRenderRichAnimation()) |
| return; |
| |
| ink_drop_highlight()->FadeIn(base::TimeDelta::FromSeconds(1)); |
| DestroyHighlight(); |
| EXPECT_EQ(1, observer()->last_animation_started_ordinal()); |
| EXPECT_EQ(2, observer()->last_animation_ended_ordinal()); |
| EXPECT_EQ(InkDropHighlight::FADE_IN, |
| observer()->last_animation_ended_context()); |
| EXPECT_EQ(InkDropAnimationEndedReason::PRE_EMPTED, |
| observer()->last_animation_ended_reason()); |
| } |
| |
| // Confirms a zero sized highlight doesn't crash. |
| TEST_F(InkDropHighlightTest, AnimatingAZeroSizeHighlight) { |
| InitHighlight(std::make_unique<InkDropHighlight>( |
| gfx::Size(0, 0), 3, gfx::PointF(), SK_ColorBLACK)); |
| ink_drop_highlight()->FadeOut(base::TimeDelta::FromMilliseconds(0), |
| false /* explode */); |
| } |
| |
| TEST_F(InkDropHighlightTest, TransformIsPixelAligned) { |
| const float kEpsilon = 0.001f; |
| gfx::Size highlight_size(10, 10); |
| InitHighlight(std::make_unique<InkDropHighlight>( |
| highlight_size, 3, gfx::PointF(3.5f, 3.5f), SK_ColorYELLOW)); |
| const gfx::PointF layer_origin( |
| ink_drop_highlight()->layer()->bounds().origin()); |
| for (auto dsf : {1.25, 1.33, 1.5, 1.6, 1.75, 1.8, 2.25}) { |
| SCOPED_TRACE(testing::Message() |
| << std::endl |
| << "Device Scale Factor: " << dsf << std::endl); |
| ink_drop_highlight()->layer()->OnDeviceScaleFactorChanged(dsf); |
| |
| const gfx::SizeF size(highlight_size); |
| gfx::Transform transform = test_api()->CalculateTransform(size); |
| gfx::Point3F transformed_layer_origin(layer_origin.x(), layer_origin.y(), |
| 0); |
| transform.TransformPoint(&transformed_layer_origin); |
| |
| // Apply device scale factor to get the final offset. |
| gfx::Transform dsf_transform; |
| dsf_transform.Scale(dsf, dsf); |
| dsf_transform.TransformPoint(&transformed_layer_origin); |
| EXPECT_NEAR(transformed_layer_origin.x(), |
| gfx::ToRoundedInt(transformed_layer_origin.x()), kEpsilon); |
| EXPECT_NEAR(transformed_layer_origin.y(), |
| gfx::ToRoundedInt(transformed_layer_origin.y()), kEpsilon); |
| } |
| } |
| |
| } // namespace test |
| } // namespace views |