// 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_element.h" #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" #include "base/time/time.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/compositor/layer_animation_delegate.h" #include "ui/compositor/scoped_animation_duration_scale_mode.h" #include "ui/compositor/test/test_layer_animation_delegate.h" #include "ui/compositor/test/test_utils.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/transform.h" namespace ui { namespace { // Verify that the TargetValue(TestLayerAnimationDelegate*) constructor // correctly assigns values. See www.crbug.com/483134. TEST(TargetValueTest, VerifyLayerAnimationDelegateConstructor) { const gfx::Rect kBounds(1, 2, 3, 5); const gfx::Transform kTransform(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f); const float kOpacity = 1.235f; const bool kVisibility = false; const float kBrightness = 2.358f; const float kGrayscale = 2.5813f; const SkColor kColor = SK_ColorCYAN; TestLayerAnimationDelegate delegate; delegate.SetBoundsFromAnimation(kBounds); delegate.SetTransformFromAnimation(kTransform); delegate.SetOpacityFromAnimation(kOpacity); delegate.SetVisibilityFromAnimation(kVisibility); delegate.SetBrightnessFromAnimation(kBrightness); delegate.SetGrayscaleFromAnimation(kGrayscale); delegate.SetColorFromAnimation(kColor); LayerAnimationElement::TargetValue target_value(&delegate); EXPECT_EQ(kBounds, target_value.bounds); EXPECT_EQ(kTransform, target_value.transform); EXPECT_FLOAT_EQ(kOpacity, target_value.opacity); EXPECT_EQ(kVisibility, target_value.visibility); EXPECT_FLOAT_EQ(kBrightness, target_value.brightness); EXPECT_FLOAT_EQ(kGrayscale, target_value.grayscale); EXPECT_EQ(SK_ColorCYAN, target_value.color); } // Check that the transformation element progresses the delegate as expected and // that the element can be reused after it completes. TEST(LayerAnimationElementTest, TransformElement) { TestLayerAnimationDelegate delegate; gfx::Transform start_transform, target_transform; start_transform.Rotate(-30.0); target_transform.Rotate(30.0); base::TimeTicks start_time; base::TimeTicks effective_start_time; base::TimeDelta delta = base::TimeDelta::FromSeconds(1); scoped_ptr element( LayerAnimationElement::CreateTransformElement(target_transform, delta)); element->set_animation_group_id(1); for (int i = 0; i < 2; ++i) { start_time = effective_start_time + delta; element->set_requested_start_time(start_time); delegate.SetTransformFromAnimation(start_transform); element->Start(&delegate, 1); element->Progress(start_time, &delegate); CheckApproximatelyEqual(start_transform, delegate.GetTransformForAnimation()); effective_start_time = start_time + delta; element->set_effective_start_time(effective_start_time); element->Progress(effective_start_time, &delegate); EXPECT_FLOAT_EQ(0.0, element->last_progressed_fraction()); element->Progress(effective_start_time + delta/2, &delegate); EXPECT_FLOAT_EQ(0.5, element->last_progressed_fraction()); base::TimeDelta element_duration; EXPECT_TRUE(element->IsFinished(effective_start_time + delta, &element_duration)); EXPECT_EQ(2 * delta, element_duration); element->Progress(effective_start_time + delta, &delegate); EXPECT_FLOAT_EQ(1.0, element->last_progressed_fraction()); CheckApproximatelyEqual(target_transform, delegate.GetTransformForAnimation()); } LayerAnimationElement::TargetValue target_value(&delegate); element->GetTargetValue(&target_value); CheckApproximatelyEqual(target_transform, target_value.transform); } // Ensures that duration is copied correctly. TEST(LayerAnimationElementTest, InverseElementDurationNoScale) { gfx::Transform transform; base::TimeDelta delta; scoped_ptr base_element( LayerAnimationElement::CreateTransformElement(transform, delta)); scoped_ptr inverse_element( LayerAnimationElement::CreateInverseTransformElement(transform, base_element.get())); EXPECT_EQ(base_element->duration(), inverse_element->duration()); } // Ensures that duration is copied correctly and not double scaled. TEST(LayerAnimationElementTest, InverseElementDurationScaled) { gfx::Transform transform; base::TimeDelta delta; ScopedAnimationDurationScaleMode faster_duration( ScopedAnimationDurationScaleMode::FAST_DURATION); scoped_ptr base_element( LayerAnimationElement::CreateTransformElement(transform, delta)); scoped_ptr inverse_element( LayerAnimationElement::CreateInverseTransformElement(transform, base_element.get())); EXPECT_EQ(base_element->duration(), inverse_element->duration()); } // Ensures that the GetTargetTransform() method works as intended. TEST(LayerAnimationElementTest, InverseElementTargetCalculation) { base::TimeTicks start_time; base::TimeDelta delta = base::TimeDelta::FromSeconds(1); start_time += delta; gfx::Transform identity, transform; transform.Scale3d(2.0, 2.0, 2.0); scoped_ptr base_element( LayerAnimationElement::CreateTransformElement(transform, delta)); scoped_ptr inverse_element( LayerAnimationElement::CreateInverseTransformElement(identity, base_element.get())); base_element->set_requested_start_time(start_time); inverse_element->set_requested_start_time(start_time); TestLayerAnimationDelegate delegate; delegate.SetTransformFromAnimation(transform); base_element->Start(&delegate, 1); inverse_element->Start(&delegate, 1); LayerAnimationElement::TargetValue target; inverse_element->GetTargetValue(&target); EXPECT_TRUE(target.transform.IsIdentity()) << "Target should be identity such that the initial 2x scale from the start" << " carries over at end when parent is doubled."; } // Check that the bounds element progresses the delegate as expected and // that the element can be reused after it completes. TEST(LayerAnimationElementTest, BoundsElement) { TestLayerAnimationDelegate delegate; gfx::Rect start, target, middle; start = target = middle = gfx::Rect(0, 0, 50, 50); start.set_x(-90); target.set_x(90); base::TimeTicks start_time; base::TimeDelta delta = base::TimeDelta::FromSeconds(1); scoped_ptr element( LayerAnimationElement::CreateBoundsElement(target, delta)); for (int i = 0; i < 2; ++i) { start_time += delta; element->set_requested_start_time(start_time); delegate.SetBoundsFromAnimation(start); element->Start(&delegate, 1); element->Progress(start_time, &delegate); CheckApproximatelyEqual(start, delegate.GetBoundsForAnimation()); element->Progress(start_time + delta/2, &delegate); CheckApproximatelyEqual(middle, delegate.GetBoundsForAnimation()); base::TimeDelta element_duration; EXPECT_TRUE(element->IsFinished(start_time + delta, &element_duration)); EXPECT_EQ(delta, element_duration); element->Progress(start_time + delta, &delegate); CheckApproximatelyEqual(target, delegate.GetBoundsForAnimation()); } LayerAnimationElement::TargetValue target_value(&delegate); element->GetTargetValue(&target_value); CheckApproximatelyEqual(target, target_value.bounds); } // Check that the opacity element progresses the delegate as expected and // that the element can be reused after it completes. TEST(LayerAnimationElementTest, OpacityElement) { TestLayerAnimationDelegate delegate; float start = 0.0; float middle = 0.5; float target = 1.0; base::TimeTicks start_time; base::TimeTicks effective_start_time; base::TimeDelta delta = base::TimeDelta::FromSeconds(1); scoped_ptr element( LayerAnimationElement::CreateOpacityElement(target, delta)); for (int i = 0; i < 2; ++i) { start_time = effective_start_time + delta; element->set_requested_start_time(start_time); delegate.SetOpacityFromAnimation(start); element->Start(&delegate, 1); element->Progress(start_time, &delegate); EXPECT_FLOAT_EQ(start, element->last_progressed_fraction()); effective_start_time = start_time + delta; element->set_effective_start_time(effective_start_time); element->Progress(effective_start_time, &delegate); EXPECT_FLOAT_EQ(start, element->last_progressed_fraction()); element->Progress(effective_start_time + delta/2, &delegate); EXPECT_FLOAT_EQ(middle, element->last_progressed_fraction()); base::TimeDelta element_duration; EXPECT_TRUE(element->IsFinished(effective_start_time + delta, &element_duration)); EXPECT_EQ(2 * delta, element_duration); element->Progress(effective_start_time + delta, &delegate); EXPECT_FLOAT_EQ(target, element->last_progressed_fraction()); EXPECT_FLOAT_EQ(target, delegate.GetOpacityForAnimation()); } LayerAnimationElement::TargetValue target_value(&delegate); element->GetTargetValue(&target_value); EXPECT_FLOAT_EQ(target, target_value.opacity); } // Check that the visibility element progresses the delegate as expected and // that the element can be reused after it completes. TEST(LayerAnimationElementTest, VisibilityElement) { TestLayerAnimationDelegate delegate; bool start = true; bool target = false; base::TimeTicks start_time; base::TimeDelta delta = base::TimeDelta::FromSeconds(1); scoped_ptr element( LayerAnimationElement::CreateVisibilityElement(target, delta)); for (int i = 0; i < 2; ++i) { start_time += delta; element->set_requested_start_time(start_time); delegate.SetVisibilityFromAnimation(start); element->Start(&delegate, 1); element->Progress(start_time, &delegate); EXPECT_TRUE(delegate.GetVisibilityForAnimation()); element->Progress(start_time + delta/2, &delegate); EXPECT_TRUE(delegate.GetVisibilityForAnimation()); base::TimeDelta element_duration; EXPECT_TRUE(element->IsFinished(start_time + delta, &element_duration)); EXPECT_EQ(delta, element_duration); element->Progress(start_time + delta, &delegate); EXPECT_FALSE(delegate.GetVisibilityForAnimation()); } LayerAnimationElement::TargetValue target_value(&delegate); element->GetTargetValue(&target_value); EXPECT_FALSE(target_value.visibility); } // Check that the Brightness element progresses the delegate as expected and // that the element can be reused after it completes. TEST(LayerAnimationElementTest, BrightnessElement) { TestLayerAnimationDelegate delegate; float start = 0.0; float middle = 0.5; float target = 1.0; base::TimeTicks start_time; base::TimeDelta delta = base::TimeDelta::FromSeconds(1); scoped_ptr element( LayerAnimationElement::CreateBrightnessElement(target, delta)); for (int i = 0; i < 2; ++i) { start_time += delta; element->set_requested_start_time(start_time); delegate.SetBrightnessFromAnimation(start); element->Start(&delegate, 1); element->Progress(start_time, &delegate); EXPECT_FLOAT_EQ(start, delegate.GetBrightnessForAnimation()); element->Progress(start_time + delta/2, &delegate); EXPECT_FLOAT_EQ(middle, delegate.GetBrightnessForAnimation()); base::TimeDelta element_duration; EXPECT_TRUE(element->IsFinished(start_time + delta, &element_duration)); EXPECT_EQ(delta, element_duration); element->Progress(start_time + delta, &delegate); EXPECT_FLOAT_EQ(target, delegate.GetBrightnessForAnimation()); } LayerAnimationElement::TargetValue target_value(&delegate); element->GetTargetValue(&target_value); EXPECT_FLOAT_EQ(target, target_value.brightness); } // Check that the Grayscale element progresses the delegate as expected and // that the element can be reused after it completes. TEST(LayerAnimationElementTest, GrayscaleElement) { TestLayerAnimationDelegate delegate; float start = 0.0; float middle = 0.5; float target = 1.0; base::TimeTicks start_time; base::TimeDelta delta = base::TimeDelta::FromSeconds(1); scoped_ptr element( LayerAnimationElement::CreateGrayscaleElement(target, delta)); for (int i = 0; i < 2; ++i) { start_time += delta; element->set_requested_start_time(start_time); delegate.SetGrayscaleFromAnimation(start); element->Start(&delegate, 1); element->Progress(start_time, &delegate); EXPECT_FLOAT_EQ(start, delegate.GetGrayscaleForAnimation()); element->Progress(start_time + delta/2, &delegate); EXPECT_FLOAT_EQ(middle, delegate.GetGrayscaleForAnimation()); base::TimeDelta element_duration; EXPECT_TRUE(element->IsFinished(start_time + delta, &element_duration)); EXPECT_EQ(delta, element_duration); element->Progress(start_time + delta, &delegate); EXPECT_FLOAT_EQ(target, delegate.GetGrayscaleForAnimation()); } LayerAnimationElement::TargetValue target_value(&delegate); element->GetTargetValue(&target_value); EXPECT_FLOAT_EQ(target, target_value.grayscale); } // Check that the pause element progresses the delegate as expected and // that the element can be reused after it completes. TEST(LayerAnimationElementTest, PauseElement) { LayerAnimationElement::AnimatableProperties properties = LayerAnimationElement::TRANSFORM | LayerAnimationElement::BOUNDS | LayerAnimationElement::OPACITY | LayerAnimationElement::BRIGHTNESS | LayerAnimationElement::GRAYSCALE; base::TimeTicks start_time; base::TimeDelta delta = base::TimeDelta::FromSeconds(1); scoped_ptr element( LayerAnimationElement::CreatePauseElement(properties, delta)); TestLayerAnimationDelegate delegate; TestLayerAnimationDelegate copy = delegate; start_time += delta; element->set_requested_start_time(start_time); element->Start(&delegate, 1); // Pause should last for |delta|. base::TimeDelta element_duration; EXPECT_TRUE(element->IsFinished(start_time + delta, &element_duration)); EXPECT_EQ(delta, element_duration); element->Progress(start_time + delta, &delegate); // Nothing should have changed. CheckApproximatelyEqual(delegate.GetBoundsForAnimation(), copy.GetBoundsForAnimation()); CheckApproximatelyEqual(delegate.GetTransformForAnimation(), copy.GetTransformForAnimation()); EXPECT_FLOAT_EQ(delegate.GetOpacityForAnimation(), copy.GetOpacityForAnimation()); EXPECT_FLOAT_EQ(delegate.GetBrightnessForAnimation(), copy.GetBrightnessForAnimation()); EXPECT_FLOAT_EQ(delegate.GetGrayscaleForAnimation(), copy.GetGrayscaleForAnimation()); } // Check that a threaded opacity element updates the delegate as expected when // aborted. TEST(LayerAnimationElementTest, AbortOpacityElement) { TestLayerAnimationDelegate delegate; float start = 0.0; float target = 1.0; base::TimeTicks start_time; base::TimeTicks effective_start_time; base::TimeDelta delta = base::TimeDelta::FromSeconds(1); scoped_ptr element( LayerAnimationElement::CreateOpacityElement(target, delta)); // Choose a non-linear Tween type. gfx::Tween::Type tween_type = gfx::Tween::EASE_IN; element->set_tween_type(tween_type); delegate.SetOpacityFromAnimation(start); // Aborting the element before it has started should not update the delegate. element->Abort(&delegate); EXPECT_FLOAT_EQ(start, delegate.GetOpacityForAnimation()); start_time += delta; element->set_requested_start_time(start_time); element->Start(&delegate, 1); element->Progress(start_time, &delegate); effective_start_time = start_time + delta; element->set_effective_start_time(effective_start_time); element->Progress(effective_start_time, &delegate); element->Progress(effective_start_time + delta/2, &delegate); // Since the element has started, it should update the delegate when // aborted. element->Abort(&delegate); EXPECT_FLOAT_EQ(gfx::Tween::CalculateValue(tween_type, 0.5), delegate.GetOpacityForAnimation()); } // Check that a threaded transform element updates the delegate as expected when // aborted. TEST(LayerAnimationElementTest, AbortTransformElement) { TestLayerAnimationDelegate delegate; gfx::Transform start_transform, target_transform; start_transform.Rotate(-30.0); target_transform.Rotate(30.0); base::TimeTicks start_time; base::TimeTicks effective_start_time; base::TimeDelta delta = base::TimeDelta::FromSeconds(1); scoped_ptr element( LayerAnimationElement::CreateTransformElement(target_transform, delta)); // Choose a non-linear Tween type. gfx::Tween::Type tween_type = gfx::Tween::EASE_IN; element->set_tween_type(tween_type); delegate.SetTransformFromAnimation(start_transform); // Aborting the element before it has started should not update the delegate. element->Abort(&delegate); CheckApproximatelyEqual(start_transform, delegate.GetTransformForAnimation()); start_time += delta; element->set_requested_start_time(start_time); element->Start(&delegate, 1); element->Progress(start_time, &delegate); effective_start_time = start_time + delta; element->set_effective_start_time(effective_start_time); element->Progress(effective_start_time, &delegate); element->Progress(effective_start_time + delta/2, &delegate); // Since the element has started, it should update the delegate when // aborted. element->Abort(&delegate); target_transform.Blend(start_transform, gfx::Tween::CalculateValue(tween_type, 0.5)); CheckApproximatelyEqual(target_transform, delegate.GetTransformForAnimation()); } } // namespace } // namespace ui