| // Copyright (c) 2012 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/compositor/layer_animation_sequence.h" |
| |
| #include <memory> |
| |
| #include "base/compiler_specific.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/time/time.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/compositor/layer_animation_delegate.h" |
| #include "ui/compositor/layer_animation_element.h" |
| #include "ui/compositor/test/test_layer_animation_delegate.h" |
| #include "ui/compositor/test/test_layer_animation_observer.h" |
| #include "ui/compositor/test/test_utils.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/transform.h" |
| |
| namespace ui { |
| |
| namespace { |
| |
| // Check that the sequence behaves sanely when it contains no elements. |
| TEST(LayerAnimationSequenceTest, NoElement) { |
| LayerAnimationSequence sequence; |
| base::TimeTicks start_time; |
| start_time += base::TimeDelta::FromSeconds(1); |
| sequence.set_start_time(start_time); |
| EXPECT_TRUE(sequence.IsFinished(start_time)); |
| EXPECT_EQ(static_cast<LayerAnimationElement::AnimatableProperties>( |
| LayerAnimationElement::UNKNOWN), |
| sequence.properties()); |
| EXPECT_FALSE(sequence.HasConflictingProperty(LayerAnimationElement::UNKNOWN)); |
| } |
| |
| // Check that the sequences progresses the delegate as expected when it contains |
| // a single non-threaded element. |
| TEST(LayerAnimationSequenceTest, SingleElement) { |
| LayerAnimationSequence sequence; |
| TestLayerAnimationDelegate delegate; |
| float start = 0.0f; |
| float middle = 0.5f; |
| float target = 1.0f; |
| base::TimeTicks start_time; |
| base::TimeDelta delta = base::TimeDelta::FromSeconds(1); |
| sequence.AddElement( |
| LayerAnimationElement::CreateBrightnessElement(target, delta)); |
| |
| for (int i = 0; i < 2; ++i) { |
| start_time += delta; |
| sequence.set_start_time(start_time); |
| delegate.SetBrightnessFromAnimation( |
| start, PropertyChangeReason::NOT_FROM_ANIMATION); |
| sequence.Start(&delegate); |
| sequence.Progress(start_time, &delegate); |
| EXPECT_FLOAT_EQ(start, delegate.GetBrightnessForAnimation()); |
| sequence.Progress(start_time + base::TimeDelta::FromMilliseconds(500), |
| &delegate); |
| EXPECT_FLOAT_EQ(middle, delegate.GetBrightnessForAnimation()); |
| EXPECT_TRUE(sequence.IsFinished(start_time + delta)); |
| sequence.Progress(start_time + base::TimeDelta::FromMilliseconds(1000), |
| &delegate); |
| EXPECT_FLOAT_EQ(target, delegate.GetBrightnessForAnimation()); |
| } |
| |
| EXPECT_EQ(static_cast<LayerAnimationElement::AnimatableProperties>( |
| LayerAnimationElement::BRIGHTNESS), |
| sequence.properties()); |
| } |
| |
| // Check that the sequences progresses the delegate as expected when it contains |
| // a single threaded element. |
| TEST(LayerAnimationSequenceTest, SingleThreadedElement) { |
| LayerAnimationSequence sequence; |
| TestLayerAnimationDelegate delegate; |
| float start = 0.0f; |
| float middle = 0.5f; |
| float target = 1.0f; |
| base::TimeTicks start_time; |
| base::TimeTicks effective_start; |
| base::TimeDelta delta = base::TimeDelta::FromSeconds(1); |
| sequence.AddElement( |
| LayerAnimationElement::CreateOpacityElement(target, delta)); |
| |
| for (int i = 0; i < 2; ++i) { |
| int starting_group_id = 1; |
| sequence.set_animation_group_id(starting_group_id); |
| start_time = effective_start + delta; |
| sequence.set_start_time(start_time); |
| delegate.SetOpacityFromAnimation(start, |
| PropertyChangeReason::NOT_FROM_ANIMATION); |
| sequence.Start(&delegate); |
| sequence.Progress(start_time, &delegate); |
| EXPECT_FLOAT_EQ(start, sequence.last_progressed_fraction()); |
| effective_start = start_time + delta; |
| sequence.OnThreadedAnimationStarted(effective_start, |
| cc::TargetProperty::OPACITY, |
| sequence.animation_group_id()); |
| sequence.Progress(effective_start + delta/2, &delegate); |
| EXPECT_FLOAT_EQ(middle, sequence.last_progressed_fraction()); |
| EXPECT_TRUE(sequence.IsFinished(effective_start + delta)); |
| sequence.Progress(effective_start + delta, &delegate); |
| EXPECT_FLOAT_EQ(target, sequence.last_progressed_fraction()); |
| EXPECT_FLOAT_EQ(target, delegate.GetOpacityForAnimation()); |
| } |
| |
| EXPECT_EQ(static_cast<LayerAnimationElement::AnimatableProperties>( |
| LayerAnimationElement::OPACITY), |
| sequence.properties()); |
| } |
| |
| // Check that the sequences progresses the delegate as expected when it contains |
| // multiple elements. Note, see the layer animator tests for cyclic sequences. |
| TEST(LayerAnimationSequenceTest, MultipleElement) { |
| LayerAnimationSequence sequence; |
| TestLayerAnimationDelegate delegate; |
| float start_opacity = 0.0f; |
| float target_opacity = 1.0f; |
| base::TimeTicks start_time; |
| base::TimeTicks opacity_effective_start; |
| base::TimeTicks transform_effective_start; |
| base::TimeDelta delta = base::TimeDelta::FromSeconds(1); |
| sequence.AddElement( |
| LayerAnimationElement::CreateOpacityElement(target_opacity, delta)); |
| |
| // Pause bounds for a second. |
| sequence.AddElement(LayerAnimationElement::CreatePauseElement( |
| LayerAnimationElement::BOUNDS, delta)); |
| |
| gfx::Transform start_transform, target_transform; |
| start_transform.Rotate(-30.0); |
| target_transform.Rotate(30.0); |
| |
| sequence.AddElement( |
| LayerAnimationElement::CreateTransformElement(target_transform, delta)); |
| |
| for (int i = 0; i < 2; ++i) { |
| int starting_group_id = 1; |
| sequence.set_animation_group_id(starting_group_id); |
| start_time = opacity_effective_start + 4 * delta; |
| sequence.set_start_time(start_time); |
| delegate.SetOpacityFromAnimation(start_opacity, |
| PropertyChangeReason::NOT_FROM_ANIMATION); |
| delegate.SetTransformFromAnimation( |
| start_transform, PropertyChangeReason::NOT_FROM_ANIMATION); |
| |
| sequence.Start(&delegate); |
| sequence.Progress(start_time, &delegate); |
| EXPECT_FLOAT_EQ(0.0, sequence.last_progressed_fraction()); |
| opacity_effective_start = start_time + delta; |
| EXPECT_EQ(starting_group_id, sequence.animation_group_id()); |
| sequence.OnThreadedAnimationStarted(opacity_effective_start, |
| cc::TargetProperty::OPACITY, |
| sequence.animation_group_id()); |
| sequence.Progress(opacity_effective_start + delta/2, &delegate); |
| EXPECT_FLOAT_EQ(0.5, sequence.last_progressed_fraction()); |
| sequence.Progress(opacity_effective_start + delta, &delegate); |
| EXPECT_FLOAT_EQ(target_opacity, delegate.GetOpacityForAnimation()); |
| |
| // Now at the start of the pause. |
| EXPECT_FLOAT_EQ(0.0, sequence.last_progressed_fraction()); |
| TestLayerAnimationDelegate copy = delegate; |
| |
| // In the middle of the pause -- nothing should have changed. |
| sequence.Progress(opacity_effective_start + delta + delta/2, |
| &delegate); |
| CheckApproximatelyEqual(delegate.GetBoundsForAnimation(), |
| copy.GetBoundsForAnimation()); |
| CheckApproximatelyEqual(delegate.GetTransformForAnimation(), |
| copy.GetTransformForAnimation()); |
| EXPECT_FLOAT_EQ(delegate.GetOpacityForAnimation(), |
| copy.GetOpacityForAnimation()); |
| |
| sequence.Progress(opacity_effective_start + 2 * delta, &delegate); |
| CheckApproximatelyEqual(start_transform, |
| delegate.GetTransformForAnimation()); |
| EXPECT_FLOAT_EQ(0.0, sequence.last_progressed_fraction()); |
| transform_effective_start = opacity_effective_start + 3 * delta; |
| EXPECT_NE(starting_group_id, sequence.animation_group_id()); |
| sequence.OnThreadedAnimationStarted(transform_effective_start, |
| cc::TargetProperty::TRANSFORM, |
| sequence.animation_group_id()); |
| sequence.Progress(transform_effective_start + delta/2, &delegate); |
| EXPECT_FLOAT_EQ(0.5, sequence.last_progressed_fraction()); |
| EXPECT_TRUE(sequence.IsFinished(transform_effective_start + delta)); |
| sequence.Progress(transform_effective_start + delta, &delegate); |
| CheckApproximatelyEqual(target_transform, |
| delegate.GetTransformForAnimation()); |
| } |
| |
| EXPECT_EQ( |
| static_cast<LayerAnimationElement::AnimatableProperties>( |
| LayerAnimationElement::OPACITY | LayerAnimationElement::TRANSFORM | |
| LayerAnimationElement::BOUNDS), |
| sequence.properties()); |
| } |
| |
| // Check that a sequence can still be aborted if it has cycled many times. |
| TEST(LayerAnimationSequenceTest, AbortingCyclicSequence) { |
| LayerAnimationSequence sequence; |
| TestLayerAnimationDelegate delegate; |
| float start_brightness = 0.0f; |
| float target_brightness = 1.0f; |
| base::TimeTicks start_time; |
| base::TimeDelta delta = base::TimeDelta::FromSeconds(1); |
| sequence.AddElement( |
| LayerAnimationElement::CreateBrightnessElement(target_brightness, delta)); |
| |
| sequence.AddElement( |
| LayerAnimationElement::CreateBrightnessElement(start_brightness, delta)); |
| |
| sequence.set_is_cyclic(true); |
| |
| delegate.SetBrightnessFromAnimation(start_brightness, |
| PropertyChangeReason::NOT_FROM_ANIMATION); |
| |
| start_time += delta; |
| sequence.set_start_time(start_time); |
| sequence.Start(&delegate); |
| sequence.Progress(start_time + base::TimeDelta::FromMilliseconds(101000), |
| &delegate); |
| EXPECT_FLOAT_EQ(target_brightness, delegate.GetBrightnessForAnimation()); |
| sequence.Abort(&delegate); |
| |
| // Should be able to reuse the sequence after aborting. |
| delegate.SetBrightnessFromAnimation(start_brightness, |
| PropertyChangeReason::NOT_FROM_ANIMATION); |
| start_time += base::TimeDelta::FromMilliseconds(101000); |
| sequence.set_start_time(start_time); |
| sequence.Progress(start_time + base::TimeDelta::FromMilliseconds(100000), |
| &delegate); |
| EXPECT_FLOAT_EQ(start_brightness, delegate.GetBrightnessForAnimation()); |
| } |
| |
| // Check that a sequence can be 'fast-forwarded' to the end and the target set. |
| // Also check that this has no effect if the sequence is cyclic. |
| TEST(LayerAnimationSequenceTest, SetTarget) { |
| LayerAnimationSequence sequence; |
| TestLayerAnimationDelegate delegate; |
| float start_opacity = 0.0f; |
| float target_opacity = 1.0f; |
| base::TimeDelta delta = base::TimeDelta::FromSeconds(1); |
| sequence.AddElement( |
| LayerAnimationElement::CreateOpacityElement(target_opacity, delta)); |
| |
| LayerAnimationElement::TargetValue target_value(&delegate); |
| target_value.opacity = start_opacity; |
| sequence.GetTargetValue(&target_value); |
| EXPECT_FLOAT_EQ(target_opacity, target_value.opacity); |
| |
| sequence.set_is_cyclic(true); |
| target_value.opacity = start_opacity; |
| sequence.GetTargetValue(&target_value); |
| EXPECT_FLOAT_EQ(start_opacity, target_value.opacity); |
| } |
| |
| TEST(LayerAnimationSequenceTest, AddObserver) { |
| base::TimeTicks start_time; |
| base::TimeDelta delta = base::TimeDelta::FromSeconds(1); |
| LayerAnimationSequence sequence; |
| sequence.AddElement( |
| LayerAnimationElement::CreateBrightnessElement(1.0f, delta)); |
| for (int i = 0; i < 2; ++i) { |
| start_time += delta; |
| sequence.set_start_time(start_time); |
| TestLayerAnimationObserver observer; |
| TestLayerAnimationDelegate delegate; |
| sequence.AddObserver(&observer); |
| EXPECT_TRUE(!observer.last_ended_sequence()); |
| sequence.Progress(start_time + delta, &delegate); |
| EXPECT_EQ(observer.last_ended_sequence(), &sequence); |
| sequence.RemoveObserver(&observer); |
| } |
| } |
| |
| TEST(LayerAnimationSequenceTest, ToString) { |
| base::TimeDelta delta = base::TimeDelta::FromSeconds(1); |
| LayerAnimationSequence sequence; |
| EXPECT_EQ( |
| "LayerAnimationSequence{size=0, properties=, elements=[], is_cyclic=0, " |
| "group_id=0}", |
| sequence.ToString()); |
| |
| std::unique_ptr<LayerAnimationElement> brightness = |
| LayerAnimationElement::CreateBrightnessElement(1.0f, delta); |
| int brightness_id = brightness->keyframe_model_id(); |
| sequence.AddElement(std::move(brightness)); |
| EXPECT_EQ( |
| base::StringPrintf( |
| "LayerAnimationSequence{size=1, properties=BRIGHTNESS, " |
| "elements=[LayerAnimationElement{name=BrightnessTransition, id=%d, " |
| "group=0, last_progressed_fraction=0.00}], " |
| "is_cyclic=0, group_id=0}", |
| brightness_id), |
| sequence.ToString()); |
| |
| std::unique_ptr<LayerAnimationElement> opacity = |
| LayerAnimationElement::CreateOpacityElement(1.0f, delta); |
| int opacity_id = opacity->keyframe_model_id(); |
| sequence.AddElement(std::move(opacity)); |
| sequence.set_is_cyclic(true); |
| sequence.set_animation_group_id(1973); |
| EXPECT_EQ( |
| base::StringPrintf( |
| "LayerAnimationSequence{size=2, properties=OPACITY|BRIGHTNESS, " |
| "elements=[LayerAnimationElement{name=BrightnessTransition, id=%d, " |
| "group=0, last_progressed_fraction=0.00}, " |
| "LayerAnimationElement{name=ThreadedOpacityTransition, id=%d, " |
| "group=0, " |
| "last_progressed_fraction=0.00}], is_cyclic=1, " |
| "group_id=1973}", |
| brightness_id, opacity_id), |
| sequence.ToString()); |
| } |
| |
| } // namespace |
| |
| } // namespace ui |