// 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 "cc/animation/animation.h"

#include <memory>

#include "base/strings/stringprintf.h"
#include "cc/animation/animation_delegate.h"
#include "cc/animation/animation_host.h"
#include "cc/animation/animation_timeline.h"
#include "cc/animation/element_animations.h"
#include "cc/animation/keyframe_effect.h"
#include "cc/animation/keyframed_animation_curve.h"
#include "cc/test/animation_test_common.h"
#include "cc/test/animation_timelines_test_common.h"

namespace cc {
namespace {

class AnimationTest : public AnimationTimelinesTest {
 public:
  AnimationTest() : animation_(Animation::Create(animation_id_)) {
    keyframe_effect_id_ = animation_->NextKeyframeEffectId();
  }
  ~AnimationTest() override = default;

  void CheckKeyframeEffectAndTimelineNeedsPushProperties(
      bool needs_push_properties,
      KeyframeEffectId keyframe_effect_id) const {
    KeyframeEffect* keyframe_effect =
        animation_->GetKeyframeEffectById(keyframe_effect_id);
    EXPECT_EQ(keyframe_effect->needs_push_properties(), needs_push_properties);
    EXPECT_EQ(timeline_->needs_push_properties(), needs_push_properties);
  }

 protected:
  scoped_refptr<Animation> animation_;
  scoped_refptr<Animation> animation_impl_;
  KeyframeEffectId keyframe_effect_id_;
};
// See element_animations_unittest.cc for active/pending observers tests.

TEST_F(AnimationTest, AttachDetachLayerIfTimelineAttached) {
  animation_->AddKeyframeEffect(
      std::make_unique<KeyframeEffect>(keyframe_effect_id_));
  ASSERT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id_));
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                   ->needs_push_properties());
  CheckKeyframeEffectAndTimelineNeedsPushProperties(false, keyframe_effect_id_);

  host_->AddAnimationTimeline(timeline_);
  EXPECT_TRUE(timeline_->needs_push_properties());

  timeline_->AttachAnimation(animation_);
  EXPECT_FALSE(animation_->element_animations(keyframe_effect_id_));
  EXPECT_FALSE(animation_->element_id_of_keyframe_effect(keyframe_effect_id_));
  EXPECT_TRUE(timeline_->needs_push_properties());
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                   ->needs_push_properties());

  host_->PushPropertiesTo(host_impl_);

  EXPECT_FALSE(GetImplKeyframeEffectForLayerId(element_id_));

  timeline_impl_ = host_impl_->GetTimelineById(timeline_id_);
  EXPECT_TRUE(timeline_impl_);
  animation_impl_ = timeline_impl_->GetAnimationById(animation_id_);
  EXPECT_TRUE(animation_impl_);

  EXPECT_FALSE(animation_impl_->element_animations(keyframe_effect_id_));
  EXPECT_FALSE(
      animation_impl_->element_id_of_keyframe_effect(keyframe_effect_id_));
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                   ->needs_push_properties());
  EXPECT_FALSE(timeline_->needs_push_properties());

  animation_->AttachElementForKeyframeEffect(element_id_, keyframe_effect_id_);
  EXPECT_EQ(animation_->GetKeyframeEffectById(keyframe_effect_id_),
            GetKeyframeEffectForElementId(element_id_));
  EXPECT_TRUE(animation_->element_animations(keyframe_effect_id_));
  EXPECT_EQ(animation_->element_id_of_keyframe_effect(keyframe_effect_id_),
            element_id_);
  CheckKeyframeEffectAndTimelineNeedsPushProperties(true, keyframe_effect_id_);

  host_->PushPropertiesTo(host_impl_);

  EXPECT_EQ(animation_impl_->GetKeyframeEffectById(keyframe_effect_id_),
            GetImplKeyframeEffectForLayerId(element_id_));
  EXPECT_TRUE(animation_impl_->element_animations(keyframe_effect_id_));
  EXPECT_EQ(animation_impl_->element_id_of_keyframe_effect(keyframe_effect_id_),
            element_id_);
  CheckKeyframeEffectAndTimelineNeedsPushProperties(false, keyframe_effect_id_);

  animation_->DetachElement();
  EXPECT_FALSE(GetKeyframeEffectForElementId(element_id_));
  EXPECT_FALSE(animation_->element_animations(keyframe_effect_id_));
  EXPECT_FALSE(animation_->element_id_of_keyframe_effect(keyframe_effect_id_));
  CheckKeyframeEffectAndTimelineNeedsPushProperties(true, keyframe_effect_id_);

  host_->PushPropertiesTo(host_impl_);

  EXPECT_FALSE(GetImplKeyframeEffectForLayerId(element_id_));
  EXPECT_FALSE(animation_impl_->element_animations(keyframe_effect_id_));
  EXPECT_FALSE(
      animation_impl_->element_id_of_keyframe_effect(keyframe_effect_id_));
  CheckKeyframeEffectAndTimelineNeedsPushProperties(false, keyframe_effect_id_);

  timeline_->DetachAnimation(animation_);
  EXPECT_FALSE(animation_->animation_timeline());
  EXPECT_FALSE(animation_->element_animations(keyframe_effect_id_));
  EXPECT_FALSE(animation_->element_id_of_keyframe_effect(keyframe_effect_id_));
  EXPECT_TRUE(timeline_->needs_push_properties());
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                   ->needs_push_properties());
  host_->PushPropertiesTo(host_impl_);
  CheckKeyframeEffectAndTimelineNeedsPushProperties(false, keyframe_effect_id_);
}

TEST_F(AnimationTest, AttachDetachTimelineIfLayerAttached) {
  host_->AddAnimationTimeline(timeline_);

  animation_->AddKeyframeEffect(
      std::make_unique<KeyframeEffect>(keyframe_effect_id_));
  ASSERT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id_));

  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                   ->element_animations());
  EXPECT_FALSE(
      animation_->GetKeyframeEffectById(keyframe_effect_id_)->element_id());
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                   ->needs_push_properties());

  animation_->AttachElementForKeyframeEffect(element_id_, keyframe_effect_id_);
  EXPECT_FALSE(animation_->animation_timeline());
  EXPECT_FALSE(GetKeyframeEffectForElementId(element_id_));
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                   ->element_animations());
  EXPECT_EQ(
      animation_->GetKeyframeEffectById(keyframe_effect_id_)->element_id(),
      element_id_);
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                   ->needs_push_properties());

  timeline_->AttachAnimation(animation_);
  EXPECT_EQ(timeline_, animation_->animation_timeline());
  EXPECT_EQ(animation_->GetKeyframeEffectById(keyframe_effect_id_),
            GetKeyframeEffectForElementId(element_id_));
  EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                  ->element_animations());
  EXPECT_EQ(
      animation_->GetKeyframeEffectById(keyframe_effect_id_)->element_id(),
      element_id_);
  EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                  ->needs_push_properties());

  // Removing animation from timeline detaches layer.
  timeline_->DetachAnimation(animation_);
  EXPECT_FALSE(animation_->animation_timeline());
  EXPECT_FALSE(GetKeyframeEffectForElementId(element_id_));
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                   ->element_animations());
  EXPECT_FALSE(
      animation_->GetKeyframeEffectById(keyframe_effect_id_)->element_id());
  EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                  ->needs_push_properties());
}

TEST_F(AnimationTest, PropertiesMutate) {
  client_.RegisterElement(element_id_, ElementListType::ACTIVE);
  client_impl_.RegisterElement(element_id_, ElementListType::PENDING);
  client_impl_.RegisterElement(element_id_, ElementListType::ACTIVE);

  host_->AddAnimationTimeline(timeline_);

  animation_->AddKeyframeEffect(
      std::make_unique<KeyframeEffect>(keyframe_effect_id_));
  ASSERT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id_));

  timeline_->AttachAnimation(animation_);
  animation_->AttachElementForKeyframeEffect(element_id_, keyframe_effect_id_);
  CheckKeyframeEffectAndTimelineNeedsPushProperties(true, keyframe_effect_id_);

  host_->PushPropertiesTo(host_impl_);
  CheckKeyframeEffectAndTimelineNeedsPushProperties(false, keyframe_effect_id_);

  const float start_opacity = .7f;
  const float end_opacity = .3f;

  const float start_brightness = .6f;
  const float end_brightness = .4f;

  const int transform_x = 10;
  const int transform_y = 20;

  const double duration = 1.;

  AddOpacityTransitionToAnimation(animation_.get(), duration, start_opacity,
                                  end_opacity, false, keyframe_effect_id_);

  AddAnimatedTransformToAnimation(animation_.get(), duration, transform_x,
                                  transform_y, keyframe_effect_id_);
  AddAnimatedFilterToAnimation(animation_.get(), duration, start_brightness,
                               end_brightness, keyframe_effect_id_);
  CheckKeyframeEffectAndTimelineNeedsPushProperties(true, keyframe_effect_id_);

  host_->PushPropertiesTo(host_impl_);
  CheckKeyframeEffectAndTimelineNeedsPushProperties(false, keyframe_effect_id_);

  EXPECT_FALSE(client_.IsPropertyMutated(element_id_, ElementListType::ACTIVE,
                                         TargetProperty::OPACITY));
  EXPECT_FALSE(client_.IsPropertyMutated(element_id_, ElementListType::ACTIVE,
                                         TargetProperty::TRANSFORM));
  EXPECT_FALSE(client_.IsPropertyMutated(element_id_, ElementListType::ACTIVE,
                                         TargetProperty::FILTER));

  EXPECT_FALSE(client_impl_.IsPropertyMutated(
      element_id_, ElementListType::ACTIVE, TargetProperty::OPACITY));
  EXPECT_FALSE(client_impl_.IsPropertyMutated(
      element_id_, ElementListType::ACTIVE, TargetProperty::TRANSFORM));
  EXPECT_FALSE(client_impl_.IsPropertyMutated(
      element_id_, ElementListType::ACTIVE, TargetProperty::FILTER));

  host_impl_->ActivateAnimations();

  base::TimeTicks time;
  time += base::TimeDelta::FromSecondsD(0.1);
  TickAnimationsTransferEvents(time, 3u);
  CheckKeyframeEffectAndTimelineNeedsPushProperties(false, keyframe_effect_id_);

  time += base::TimeDelta::FromSecondsD(duration);
  TickAnimationsTransferEvents(time, 3u);
  CheckKeyframeEffectAndTimelineNeedsPushProperties(true, keyframe_effect_id_);

  client_.ExpectOpacityPropertyMutated(element_id_, ElementListType::ACTIVE,
                                       end_opacity);
  client_.ExpectTransformPropertyMutated(element_id_, ElementListType::ACTIVE,
                                         transform_x, transform_y);
  client_.ExpectFilterPropertyMutated(element_id_, ElementListType::ACTIVE,
                                      end_brightness);

  client_impl_.ExpectOpacityPropertyMutated(
      element_id_, ElementListType::ACTIVE, end_opacity);
  client_impl_.ExpectTransformPropertyMutated(
      element_id_, ElementListType::ACTIVE, transform_x, transform_y);
  client_impl_.ExpectFilterPropertyMutated(element_id_, ElementListType::ACTIVE,
                                           end_brightness);

  client_impl_.ExpectOpacityPropertyMutated(
      element_id_, ElementListType::PENDING, end_opacity);
  client_impl_.ExpectTransformPropertyMutated(
      element_id_, ElementListType::PENDING, transform_x, transform_y);
  client_impl_.ExpectFilterPropertyMutated(
      element_id_, ElementListType::PENDING, end_brightness);
}

TEST_F(AnimationTest, AttachTwoAnimationsToOneLayer) {
  TestAnimationDelegate delegate1;
  TestAnimationDelegate delegate2;

  client_.RegisterElement(element_id_, ElementListType::ACTIVE);
  client_impl_.RegisterElement(element_id_, ElementListType::PENDING);
  client_impl_.RegisterElement(element_id_, ElementListType::ACTIVE);

  scoped_refptr<Animation> animation1 = Animation::Create(100);
  scoped_refptr<Animation> animation2 = Animation::Create(200);

  KeyframeEffectId keyframe_effect_id1 = animation1->NextKeyframeEffectId();
  animation1->AddKeyframeEffect(
      std::make_unique<KeyframeEffect>(keyframe_effect_id1));
  ASSERT_TRUE(animation1->GetKeyframeEffectById(keyframe_effect_id1));
  KeyframeEffectId keyframe_effect_id2 = animation2->NextKeyframeEffectId();
  animation2->AddKeyframeEffect(
      std::make_unique<KeyframeEffect>(keyframe_effect_id2));
  ASSERT_TRUE(animation2->GetKeyframeEffectById(keyframe_effect_id2));

  host_->AddAnimationTimeline(timeline_);

  timeline_->AttachAnimation(animation1);
  EXPECT_TRUE(timeline_->needs_push_properties());

  timeline_->AttachAnimation(animation2);
  EXPECT_TRUE(timeline_->needs_push_properties());

  animation1->set_animation_delegate(&delegate1);
  animation2->set_animation_delegate(&delegate2);

  // Attach animations to the same layer.
  animation1->AttachElementForKeyframeEffect(element_id_, keyframe_effect_id1);
  animation2->AttachElementForKeyframeEffect(element_id_, keyframe_effect_id2);

  const float start_opacity = .7f;
  const float end_opacity = .3f;

  const int transform_x = 10;
  const int transform_y = 20;

  const double duration = 1.;

  AddOpacityTransitionToAnimation(animation1.get(), duration, start_opacity,
                                  end_opacity, false, keyframe_effect_id1);
  AddAnimatedTransformToAnimation(animation2.get(), duration, transform_x,
                                  transform_y, keyframe_effect_id2);

  host_->PushPropertiesTo(host_impl_);
  host_impl_->ActivateAnimations();

  EXPECT_FALSE(delegate1.started());
  EXPECT_FALSE(delegate1.finished());

  EXPECT_FALSE(delegate2.started());
  EXPECT_FALSE(delegate2.finished());

  base::TimeTicks time;
  time += base::TimeDelta::FromSecondsD(0.1);
  TickAnimationsTransferEvents(time, 2u);

  EXPECT_TRUE(delegate1.started());
  EXPECT_FALSE(delegate1.finished());

  EXPECT_TRUE(delegate2.started());
  EXPECT_FALSE(delegate2.finished());

  EXPECT_FALSE(animation1->GetKeyframeEffectById(keyframe_effect_id1)
                   ->needs_push_properties());
  EXPECT_FALSE(animation2->GetKeyframeEffectById(keyframe_effect_id2)
                   ->needs_push_properties());

  time += base::TimeDelta::FromSecondsD(duration);
  TickAnimationsTransferEvents(time, 2u);

  EXPECT_TRUE(delegate1.finished());
  EXPECT_TRUE(delegate2.finished());

  EXPECT_TRUE(animation1->GetKeyframeEffectById(keyframe_effect_id1)
                  ->needs_push_properties());
  EXPECT_TRUE(animation2->GetKeyframeEffectById(keyframe_effect_id2)
                  ->needs_push_properties());

  client_.ExpectOpacityPropertyMutated(element_id_, ElementListType::ACTIVE,
                                       end_opacity);
  client_.ExpectTransformPropertyMutated(element_id_, ElementListType::ACTIVE,
                                         transform_x, transform_y);

  client_impl_.ExpectOpacityPropertyMutated(
      element_id_, ElementListType::ACTIVE, end_opacity);
  client_impl_.ExpectTransformPropertyMutated(
      element_id_, ElementListType::ACTIVE, transform_x, transform_y);

  client_impl_.ExpectOpacityPropertyMutated(
      element_id_, ElementListType::PENDING, end_opacity);
  client_impl_.ExpectTransformPropertyMutated(
      element_id_, ElementListType::PENDING, transform_x, transform_y);
}

TEST_F(AnimationTest, AddRemoveAnimationToNonAttachedAnimation) {
  client_.RegisterElement(element_id_, ElementListType::ACTIVE);
  client_impl_.RegisterElement(element_id_, ElementListType::PENDING);
  client_impl_.RegisterElement(element_id_, ElementListType::ACTIVE);

  animation_->AddKeyframeEffect(
      std::make_unique<KeyframeEffect>(keyframe_effect_id_));
  ASSERT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id_));

  const double duration = 1.;
  const float start_opacity = .7f;
  const float end_opacity = .3f;

  const int filter_id = AddAnimatedFilterToAnimation(
      animation_.get(), duration, 0.1f, 0.9f, keyframe_effect_id_);
  AddOpacityTransitionToAnimation(animation_.get(), duration, start_opacity,
                                  end_opacity, false, keyframe_effect_id_);

  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                   ->needs_push_properties());

  host_->AddAnimationTimeline(timeline_);
  timeline_->AttachAnimation(animation_);

  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                   ->needs_push_properties());
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                   ->element_animations());
  animation_->RemoveKeyframeModelForKeyframeEffect(filter_id,
                                                   keyframe_effect_id_);
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                   ->needs_push_properties());

  animation_->AttachElementForKeyframeEffect(element_id_, keyframe_effect_id_);

  EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                  ->element_animations());
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                   ->element_animations()
                   ->HasAnyAnimationTargetingProperty(TargetProperty::FILTER));
  EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                  ->element_animations()
                  ->HasAnyAnimationTargetingProperty(TargetProperty::OPACITY));
  EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                  ->needs_push_properties());

  host_->PushPropertiesTo(host_impl_);

  EXPECT_FALSE(client_.IsPropertyMutated(element_id_, ElementListType::ACTIVE,
                                         TargetProperty::OPACITY));
  EXPECT_FALSE(client_impl_.IsPropertyMutated(
      element_id_, ElementListType::ACTIVE, TargetProperty::OPACITY));

  EXPECT_FALSE(client_.IsPropertyMutated(element_id_, ElementListType::ACTIVE,
                                         TargetProperty::FILTER));
  EXPECT_FALSE(client_impl_.IsPropertyMutated(
      element_id_, ElementListType::ACTIVE, TargetProperty::FILTER));

  host_impl_->ActivateAnimations();

  base::TimeTicks time;
  time += base::TimeDelta::FromSecondsD(0.1);
  TickAnimationsTransferEvents(time, 1u);

  time += base::TimeDelta::FromSecondsD(duration);
  TickAnimationsTransferEvents(time, 1u);

  client_.ExpectOpacityPropertyMutated(element_id_, ElementListType::ACTIVE,
                                       end_opacity);
  client_impl_.ExpectOpacityPropertyMutated(
      element_id_, ElementListType::ACTIVE, end_opacity);
  client_impl_.ExpectOpacityPropertyMutated(
      element_id_, ElementListType::PENDING, end_opacity);

  EXPECT_FALSE(client_.IsPropertyMutated(element_id_, ElementListType::ACTIVE,
                                         TargetProperty::FILTER));
  EXPECT_FALSE(client_impl_.IsPropertyMutated(
      element_id_, ElementListType::ACTIVE, TargetProperty::FILTER));
}

TEST_F(AnimationTest, AddRemoveAnimationCausesSetNeedsCommit) {
  client_.RegisterElement(element_id_, ElementListType::ACTIVE);
  host_->AddAnimationTimeline(timeline_);
  animation_->AddKeyframeEffect(
      std::make_unique<KeyframeEffect>(keyframe_effect_id_));
  ASSERT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id_));

  timeline_->AttachAnimation(animation_);
  animation_->AttachElementForKeyframeEffect(element_id_, keyframe_effect_id_);

  EXPECT_FALSE(client_.mutators_need_commit());

  const int keyframe_model_id = AddOpacityTransitionToAnimation(
      animation_.get(), 1., .7f, .3f, false, keyframe_effect_id_);

  EXPECT_TRUE(client_.mutators_need_commit());
  client_.set_mutators_need_commit(false);

  animation_->PauseKeyframeModelForKeyframeEffect(keyframe_model_id, 1.,
                                                  keyframe_effect_id_);
  EXPECT_TRUE(client_.mutators_need_commit());
  client_.set_mutators_need_commit(false);

  animation_->RemoveKeyframeModelForKeyframeEffect(keyframe_model_id,
                                                   keyframe_effect_id_);
  EXPECT_TRUE(client_.mutators_need_commit());
  client_.set_mutators_need_commit(false);
}

// If main-thread animation switches to another layer within one frame then
// impl-thread animation must be switched as well.
TEST_F(AnimationTest, SwitchToLayer) {
  host_->AddAnimationTimeline(timeline_);
  animation_->AddKeyframeEffect(
      std::make_unique<KeyframeEffect>(keyframe_effect_id_));
  timeline_->AttachAnimation(animation_);
  animation_->AttachElementForKeyframeEffect(element_id_, keyframe_effect_id_);

  host_->PushPropertiesTo(host_impl_);

  timeline_impl_ = host_impl_->GetTimelineById(timeline_id_);
  EXPECT_TRUE(timeline_impl_);
  animation_impl_ = timeline_impl_->GetAnimationById(animation_id_);
  EXPECT_TRUE(animation_impl_);

  EXPECT_EQ(animation_->GetKeyframeEffectById(keyframe_effect_id_),
            GetKeyframeEffectForElementId(element_id_));
  EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                  ->element_animations());
  EXPECT_EQ(
      animation_->GetKeyframeEffectById(keyframe_effect_id_)->element_id(),
      element_id_);

  timeline_impl_ = host_impl_->GetTimelineById(timeline_id_);
  EXPECT_TRUE(timeline_impl_);
  animation_impl_ = timeline_impl_->GetAnimationById(animation_id_);
  EXPECT_TRUE(animation_impl_);
  EXPECT_EQ(animation_impl_->GetKeyframeEffectById(keyframe_effect_id_),
            GetImplKeyframeEffectForLayerId(element_id_));
  EXPECT_TRUE(animation_impl_->GetKeyframeEffectById(keyframe_effect_id_)
                  ->element_animations());
  EXPECT_EQ(
      animation_impl_->GetKeyframeEffectById(keyframe_effect_id_)->element_id(),
      element_id_);
  CheckKeyframeEffectAndTimelineNeedsPushProperties(false, keyframe_effect_id_);

  const ElementId new_element_id(NextTestLayerId());
  animation_->DetachElement();
  animation_->AttachElementForKeyframeEffect(new_element_id,
                                             keyframe_effect_id_);

  EXPECT_EQ(animation_->GetKeyframeEffectById(keyframe_effect_id_),
            GetKeyframeEffectForElementId(new_element_id));
  EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id_)
                  ->element_animations());
  EXPECT_EQ(
      animation_->GetKeyframeEffectById(keyframe_effect_id_)->element_id(),
      new_element_id);
  CheckKeyframeEffectAndTimelineNeedsPushProperties(true, keyframe_effect_id_);

  host_->PushPropertiesTo(host_impl_);

  EXPECT_EQ(animation_impl_->GetKeyframeEffectById(keyframe_effect_id_),
            GetImplKeyframeEffectForLayerId(new_element_id));
  EXPECT_TRUE(animation_impl_->GetKeyframeEffectById(keyframe_effect_id_)
                  ->element_animations());
  EXPECT_EQ(
      animation_impl_->GetKeyframeEffectById(keyframe_effect_id_)->element_id(),
      new_element_id);
}

TEST_F(AnimationTest, ToString) {
  animation_->AddKeyframeEffect(
      std::make_unique<KeyframeEffect>(keyframe_effect_id_));
  animation_->AttachElementForKeyframeEffect(element_id_, keyframe_effect_id_);
  EXPECT_EQ(
      base::StringPrintf("Animation{id=%d, element_id=%s, keyframe_models=[]}",
                         animation_->id(), element_id_.ToString().c_str()),
      animation_->ToString());

  animation_->AddKeyframeModelForKeyframeEffect(
      KeyframeModel::Create(std::make_unique<FakeFloatAnimationCurve>(15), 42,
                            73, TargetProperty::OPACITY),
      keyframe_effect_id_);
  EXPECT_EQ(
      base::StringPrintf("Animation{id=%d, element_id=%s, "
                         "keyframe_models=[KeyframeModel{id=42, "
                         "group=73, target_property_id=1, "
                         "run_state=WAITING_FOR_TARGET_AVAILABILITY}]}",
                         animation_->id(), element_id_.ToString().c_str()),
      animation_->ToString());

  animation_->AddKeyframeModelForKeyframeEffect(
      KeyframeModel::Create(std::make_unique<FakeFloatAnimationCurve>(18), 45,
                            76, TargetProperty::BOUNDS),
      keyframe_effect_id_);
  EXPECT_EQ(
      base::StringPrintf(
          "Animation{id=%d, element_id=%s, "
          "keyframe_models=[KeyframeModel{id=42, "
          "group=73, target_property_id=1, "
          "run_state=WAITING_FOR_TARGET_AVAILABILITY}, KeyframeModel{id=45, "
          "group=76, "
          "target_property_id=5, run_state=WAITING_FOR_TARGET_AVAILABILITY}]}",
          animation_->id(), element_id_.ToString().c_str()),
      animation_->ToString());

  KeyframeEffectId second_keyframe_effect_id =
      animation_->NextKeyframeEffectId();
  ElementId second_element_id(NextTestLayerId());
  animation_->AddKeyframeEffect(
      std::make_unique<KeyframeEffect>(second_keyframe_effect_id));
  animation_->AttachElementForKeyframeEffect(second_element_id,
                                             second_keyframe_effect_id);
  animation_->AddKeyframeModelForKeyframeEffect(
      KeyframeModel::Create(std::make_unique<FakeFloatAnimationCurve>(20), 48,
                            78, TargetProperty::OPACITY),
      second_keyframe_effect_id);
  EXPECT_EQ(
      base::StringPrintf(
          "Animation{id=%d, element_id=%s, "
          "keyframe_models=[KeyframeModel{id=42, "
          "group=73, target_property_id=1, "
          "run_state=WAITING_FOR_TARGET_AVAILABILITY}, KeyframeModel{id=45, "
          "group=76, "
          "target_property_id=5, run_state=WAITING_FOR_TARGET_AVAILABILITY}]"
          ", element_id=%s, "
          "keyframe_models=[KeyframeModel{id=48, "
          "group=78, target_property_id=1, "
          "run_state=WAITING_FOR_TARGET_AVAILABILITY}]}",
          animation_->id(), element_id_.ToString().c_str(),
          second_element_id.ToString().c_str()),
      animation_->ToString());
}

TEST_F(AnimationTest,
       AddTwoKeyframeEffectsFromTheSameElementToOneAnimationTest) {
  host_->AddAnimationTimeline(timeline_);
  EXPECT_TRUE(timeline_->needs_push_properties());

  KeyframeEffectId keyframe_effect_id1 = animation_->NextKeyframeEffectId();

  animation_->AddKeyframeEffect(
      std::make_unique<KeyframeEffect>(keyframe_effect_id1));
  ASSERT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id1));
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id1)
                   ->needs_push_properties());

  KeyframeEffectId keyframe_effect_id2 = animation_->NextKeyframeEffectId();
  animation_->AddKeyframeEffect(
      std::make_unique<KeyframeEffect>(keyframe_effect_id2));
  ASSERT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id2));
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id2)
                   ->needs_push_properties());

  timeline_->AttachAnimation(animation_);
  EXPECT_FALSE(animation_->element_animations(keyframe_effect_id1));
  EXPECT_FALSE(animation_->element_animations(keyframe_effect_id2));
  EXPECT_FALSE(animation_->element_id_of_keyframe_effect(keyframe_effect_id1));
  EXPECT_FALSE(animation_->element_id_of_keyframe_effect(keyframe_effect_id2));
  EXPECT_TRUE(timeline_->needs_push_properties());
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id1)
                   ->needs_push_properties());
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id2)
                   ->needs_push_properties());

  host_->PushPropertiesTo(host_impl_);

  timeline_impl_ = host_impl_->GetTimelineById(timeline_id_);
  EXPECT_TRUE(timeline_impl_);
  animation_impl_ = timeline_impl_->GetAnimationById(animation_id_);
  EXPECT_TRUE(animation_impl_);

  animation_->AttachElementForKeyframeEffect(element_id_, keyframe_effect_id1);
  EXPECT_TRUE(animation_->element_animations(keyframe_effect_id1));
  EXPECT_EQ(animation_->element_id_of_keyframe_effect(keyframe_effect_id1),
            element_id_);
  EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id1)
                  ->needs_push_properties());

  EXPECT_FALSE(animation_->element_animations(keyframe_effect_id2));
  EXPECT_NE(animation_->element_id_of_keyframe_effect(keyframe_effect_id2),
            element_id_);
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id2)
                   ->needs_push_properties());

  const scoped_refptr<ElementAnimations> element_animations =
      host_->GetElementAnimationsForElementId(element_id_);
  EXPECT_TRUE(element_animations->keyframe_effects_list().HasObserver(
      animation_->GetKeyframeEffectById(keyframe_effect_id1)));
  EXPECT_FALSE(element_animations->keyframe_effects_list().HasObserver(
      animation_->GetKeyframeEffectById(keyframe_effect_id2)));

  animation_->AttachElementForKeyframeEffect(element_id_, keyframe_effect_id2);
  EXPECT_TRUE(animation_->element_animations(keyframe_effect_id2));
  EXPECT_EQ(animation_->element_id_of_keyframe_effect(keyframe_effect_id2),
            element_id_);
  EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id2)
                  ->needs_push_properties());

  EXPECT_TRUE(element_animations->keyframe_effects_list().HasObserver(
      animation_->GetKeyframeEffectById(keyframe_effect_id2)));

  host_->PushPropertiesTo(host_impl_);

  const scoped_refptr<ElementAnimations> element_animations_impl =
      host_impl_->GetElementAnimationsForElementId(element_id_);
  EXPECT_TRUE(element_animations_impl->keyframe_effects_list().HasObserver(
      animation_impl_->GetKeyframeEffectById(keyframe_effect_id1)));
  EXPECT_TRUE(element_animations_impl->keyframe_effects_list().HasObserver(
      animation_impl_->GetKeyframeEffectById(keyframe_effect_id2)));

  EXPECT_TRUE(animation_impl_->element_animations(keyframe_effect_id1));
  EXPECT_EQ(animation_impl_->element_id_of_keyframe_effect(keyframe_effect_id1),
            element_id_);
  EXPECT_TRUE(animation_impl_->element_animations(keyframe_effect_id2));
  EXPECT_EQ(animation_impl_->element_id_of_keyframe_effect(keyframe_effect_id2),
            element_id_);

  animation_->DetachElement();
  EXPECT_FALSE(animation_->element_animations(keyframe_effect_id1));
  EXPECT_FALSE(animation_->element_id_of_keyframe_effect(keyframe_effect_id1));
  EXPECT_FALSE(element_animations->keyframe_effects_list().HasObserver(
      animation_->GetKeyframeEffectById(keyframe_effect_id1)));

  EXPECT_FALSE(animation_->element_animations(keyframe_effect_id2));
  EXPECT_FALSE(animation_->element_id_of_keyframe_effect(keyframe_effect_id2));
  EXPECT_FALSE(element_animations->keyframe_effects_list().HasObserver(
      animation_->GetKeyframeEffectById(keyframe_effect_id2)));

  EXPECT_TRUE(element_animations_impl->keyframe_effects_list().HasObserver(
      animation_impl_->GetKeyframeEffectById(keyframe_effect_id1)));
  EXPECT_TRUE(element_animations_impl->keyframe_effects_list().HasObserver(
      animation_impl_->GetKeyframeEffectById(keyframe_effect_id2)));

  host_->PushPropertiesTo(host_impl_);

  EXPECT_FALSE(animation_impl_->element_animations(keyframe_effect_id1));
  EXPECT_FALSE(
      animation_impl_->element_id_of_keyframe_effect(keyframe_effect_id1));
  EXPECT_FALSE(element_animations_impl->keyframe_effects_list().HasObserver(
      animation_impl_->GetKeyframeEffectById(keyframe_effect_id1)));
  EXPECT_FALSE(animation_impl_->element_animations(keyframe_effect_id2));
  EXPECT_FALSE(
      animation_impl_->element_id_of_keyframe_effect(keyframe_effect_id2));
  EXPECT_FALSE(element_animations_impl->keyframe_effects_list().HasObserver(
      animation_impl_->GetKeyframeEffectById(keyframe_effect_id2)));

  timeline_->DetachAnimation(animation_);
  EXPECT_FALSE(animation_->animation_timeline());

  EXPECT_FALSE(animation_->element_animations(keyframe_effect_id1));
  EXPECT_FALSE(animation_->element_id_of_keyframe_effect(keyframe_effect_id1));
  EXPECT_FALSE(animation_->element_animations(keyframe_effect_id2));
  EXPECT_FALSE(animation_->element_id_of_keyframe_effect(keyframe_effect_id2));

  EXPECT_TRUE(timeline_->needs_push_properties());
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id1)
                   ->needs_push_properties());
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id2)
                   ->needs_push_properties());
}

TEST_F(AnimationTest,
       AddTwoKeyframeEffectsFromDifferentElementsToOneAnimationTest) {
  host_->AddAnimationTimeline(timeline_);

  KeyframeEffectId keyframe_effect_id1 = animation_->NextKeyframeEffectId();

  animation_->AddKeyframeEffect(
      std::make_unique<KeyframeEffect>(keyframe_effect_id1));
  ASSERT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id1));
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id1)
                   ->needs_push_properties());

  KeyframeEffectId keyframe_effect_id2 = animation_->NextKeyframeEffectId();
  animation_->AddKeyframeEffect(
      std::make_unique<KeyframeEffect>(keyframe_effect_id2));
  ASSERT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id2));
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id2)
                   ->needs_push_properties());

  EXPECT_FALSE(animation_->element_animations(keyframe_effect_id1));
  EXPECT_FALSE(animation_->element_animations(keyframe_effect_id2));
  EXPECT_FALSE(animation_->element_id_of_keyframe_effect(keyframe_effect_id1));
  EXPECT_FALSE(animation_->element_id_of_keyframe_effect(keyframe_effect_id2));
  EXPECT_TRUE(timeline_->needs_push_properties());
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id1)
                   ->needs_push_properties());
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id2)
                   ->needs_push_properties());

  ElementId element1(NextTestLayerId());
  ElementId element2(NextTestLayerId());

  animation_->AttachElementForKeyframeEffect(element1, keyframe_effect_id1);
  animation_->AttachElementForKeyframeEffect(element2, keyframe_effect_id2);

  EXPECT_FALSE(animation_->animation_timeline());

  scoped_refptr<ElementAnimations> element_animations =
      host_->GetElementAnimationsForElementId(element1);
  EXPECT_FALSE(element_animations);
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id1)
                   ->element_animations());
  EXPECT_EQ(
      animation_->GetKeyframeEffectById(keyframe_effect_id1)->element_id(),
      element1);
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id1)
                   ->needs_push_properties());

  timeline_->AttachAnimation(animation_);
  EXPECT_EQ(timeline_, animation_->animation_timeline());
  EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id1)
                  ->element_animations());
  EXPECT_EQ(
      animation_->GetKeyframeEffectById(keyframe_effect_id1)->element_id(),
      element1);
  EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id1)
                  ->needs_push_properties());

  element_animations = host_->GetElementAnimationsForElementId(element1);
  EXPECT_TRUE(element_animations->keyframe_effects_list().HasObserver(
      animation_->GetKeyframeEffectById(keyframe_effect_id1)));
  EXPECT_FALSE(element_animations->keyframe_effects_list().HasObserver(
      animation_->GetKeyframeEffectById(keyframe_effect_id2)));

  element_animations = host_->GetElementAnimationsForElementId(element2);
  EXPECT_TRUE(element_animations->keyframe_effects_list().HasObserver(
      animation_->GetKeyframeEffectById(keyframe_effect_id2)));

  animation_->DetachElement();
  EXPECT_TRUE(animation_->animation_timeline());
  EXPECT_FALSE(element_animations->keyframe_effects_list().HasObserver(
      animation_->GetKeyframeEffectById(keyframe_effect_id2)));
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id1)
                   ->element_animations());
  EXPECT_FALSE(
      animation_->GetKeyframeEffectById(keyframe_effect_id1)->element_id());
  EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id1)
                  ->needs_push_properties());
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id2)
                   ->element_animations());
  EXPECT_FALSE(
      animation_->GetKeyframeEffectById(keyframe_effect_id2)->element_id());
  EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id2)
                  ->needs_push_properties());

  // Removing animation from timeline detaches layer.
  timeline_->DetachAnimation(animation_);
  EXPECT_FALSE(animation_->animation_timeline());
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id1)
                   ->element_animations());
  EXPECT_FALSE(
      animation_->GetKeyframeEffectById(keyframe_effect_id1)->element_id());
  EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id1)
                  ->needs_push_properties());
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id2)
                   ->element_animations());
  EXPECT_FALSE(
      animation_->GetKeyframeEffectById(keyframe_effect_id2)->element_id());
  EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id2)
                  ->needs_push_properties());
}

TEST_F(AnimationTest, TickingAnimationsFromTwoKeyframeEffects) {
  TestAnimationDelegate delegate1;

  client_.RegisterElement(element_id_, ElementListType::ACTIVE);
  client_impl_.RegisterElement(element_id_, ElementListType::PENDING);
  client_impl_.RegisterElement(element_id_, ElementListType::ACTIVE);

  KeyframeEffectId keyframe_effect_id1 = animation_->NextKeyframeEffectId();

  animation_->AddKeyframeEffect(
      std::make_unique<KeyframeEffect>(keyframe_effect_id1));
  ASSERT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id1));
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id1)
                   ->needs_push_properties());

  KeyframeEffectId keyframe_effect_id2 = animation_->NextKeyframeEffectId();
  animation_->AddKeyframeEffect(
      std::make_unique<KeyframeEffect>(keyframe_effect_id2));
  ASSERT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id2));
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id2)
                   ->needs_push_properties());

  host_->AddAnimationTimeline(timeline_);

  timeline_->AttachAnimation(animation_);
  EXPECT_TRUE(timeline_->needs_push_properties());

  animation_->set_animation_delegate(&delegate1);

  animation_->AttachElementForKeyframeEffect(element_id_, keyframe_effect_id1);
  animation_->AttachElementForKeyframeEffect(element_id_, keyframe_effect_id2);

  const float start_opacity = .7f;
  const float end_opacity = .3f;

  const int transform_x = 10;
  const int transform_y = 20;

  const double duration = 1.;

  AddOpacityTransitionToAnimation(animation_.get(), duration, start_opacity,
                                  end_opacity, false, keyframe_effect_id1);
  AddAnimatedTransformToAnimation(animation_.get(), duration, transform_x,
                                  transform_y, keyframe_effect_id2);
  host_->PushPropertiesTo(host_impl_);
  host_impl_->ActivateAnimations();

  EXPECT_FALSE(delegate1.started());
  EXPECT_FALSE(delegate1.finished());

  base::TimeTicks time;
  time += base::TimeDelta::FromSecondsD(0.1);
  TickAnimationsTransferEvents(time, 2u);

  EXPECT_TRUE(delegate1.started());
  EXPECT_FALSE(delegate1.finished());

  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id1)
                   ->needs_push_properties());
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id2)
                   ->needs_push_properties());

  time += base::TimeDelta::FromSecondsD(duration);
  TickAnimationsTransferEvents(time, 2u);

  EXPECT_TRUE(delegate1.finished());

  EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id1)
                  ->needs_push_properties());
  EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id2)
                  ->needs_push_properties());

  client_.ExpectOpacityPropertyMutated(element_id_, ElementListType::ACTIVE,
                                       end_opacity);
  client_.ExpectTransformPropertyMutated(element_id_, ElementListType::ACTIVE,
                                         transform_x, transform_y);

  client_impl_.ExpectOpacityPropertyMutated(
      element_id_, ElementListType::ACTIVE, end_opacity);
  client_impl_.ExpectTransformPropertyMutated(
      element_id_, ElementListType::ACTIVE, transform_x, transform_y);

  client_impl_.ExpectOpacityPropertyMutated(
      element_id_, ElementListType::PENDING, end_opacity);
  client_impl_.ExpectTransformPropertyMutated(
      element_id_, ElementListType::PENDING, transform_x, transform_y);
}

TEST_F(AnimationTest, KeyframeEffectSyncToImplTest) {
  host_->AddAnimationTimeline(timeline_);
  EXPECT_TRUE(timeline_->needs_push_properties());
  timeline_->AttachAnimation(animation_);

  KeyframeEffectId keyframe_effect_id1 = animation_->NextKeyframeEffectId();
  animation_->AddKeyframeEffect(
      std::make_unique<KeyframeEffect>(keyframe_effect_id1));
  EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id1));
  EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id1)
                   ->needs_push_properties());

  host_->PushPropertiesTo(host_impl_);

  timeline_impl_ = host_impl_->GetTimelineById(timeline_id_);
  EXPECT_TRUE(timeline_impl_);
  animation_impl_ = timeline_impl_->GetAnimationById(animation_id_);
  EXPECT_TRUE(animation_impl_);
  EXPECT_TRUE(animation_impl_->GetKeyframeEffectById(keyframe_effect_id1));

  EXPECT_FALSE(timeline_->needs_push_properties());

  KeyframeEffectId keyframe_effect_id2 = animation_->NextKeyframeEffectId();
  animation_->AddKeyframeEffect(
      std::make_unique<KeyframeEffect>(keyframe_effect_id2));
  EXPECT_TRUE(timeline_->needs_push_properties());

  host_->PushPropertiesTo(host_impl_);

  EXPECT_TRUE(animation_impl_->GetKeyframeEffectById(keyframe_effect_id2));
}

}  // namespace
}  // namespace cc
