chromium/ash/utility/layer_copy_animator_unittest.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/utility/layer_copy_animator.h"

#include "base/cancelable_callback.h"
#include "base/functional/bind.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/timer/timer.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/window.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/compositor/test/test_compositor_host.h"
#include "ui/compositor/test/test_context_factories.h"
#include "ui/compositor/test/test_layer_animation_observer.h"
#include "ui/gfx/geometry/rect.h"

namespace ash {
namespace {

class TestLayerCopyAnimator final : public LayerCopyAnimator {
 public:
  explicit TestLayerCopyAnimator(aura::Window* window)
      : LayerCopyAnimator(window) {}
  TestLayerCopyAnimator(const TestLayerCopyAnimator& animator) = delete;
  TestLayerCopyAnimator& operator=(const TestLayerCopyAnimator& animator) =
      delete;
  ~TestLayerCopyAnimator() final = default;

  // LayerCopyAnimator:
  void OnLayerCopied(std::unique_ptr<ui::Layer> new_layer) override {
    DCHECK(!copied_);
    LayerCopyAnimator::OnLayerCopied(std::move(new_layer));
    copied_ = true;
    if (run_loop_.running())
      run_loop_.Quit();
  }

  ui::Layer* WaitForCopy() {
    if (!copied_)
      run_loop_.Run();
    return copied_layer_for_test();
  }

 private:
  base::RunLoop run_loop_;
  bool copied_ = false;
};

class LayerCopyAnimatorTest : public testing::Test {
 public:
  LayerCopyAnimatorTest() = default;
  LayerCopyAnimatorTest(const LayerCopyAnimatorTest&) = delete;
  LayerCopyAnimatorTest& operator=(const LayerCopyAnimatorTest&) = delete;
  ~LayerCopyAnimatorTest() override = default;

  // testing::Test:
  void SetUp() override {
    context_factories_ = std::make_unique<ui::TestContextFactories>(false);

    const gfx::Rect bounds(300, 300);
    host_.reset(ui::TestCompositorHost::Create(
        bounds, context_factories_->GetContextFactory()));
    host_->Show();

    root_.Init(ui::LAYER_NOT_DRAWN);
    root_.SetBounds(gfx::Rect(200, 200));
    compositor()->SetRootLayer(root_.layer());

    anim_root_ = std::make_unique<aura::Window>(nullptr);
    anim_root_->Init(ui::LAYER_NOT_DRAWN);
    anim_root_->SetBounds(gfx::Rect(100, 100));

    anim_leaf_.Init(ui::LAYER_TEXTURED);
    anim_leaf_.SetBounds(gfx::Rect(50, 50));

    root_.AddChild(anim_root_.get());
    anim_root_->AddChild(&anim_leaf_);
  }

  void TearDown() override {
    DeleteAnimRoot();
    host_.reset();
    context_factories_.reset();
  }

  void Advance(const base::TimeDelta& delta) {
    task_environment_.FastForwardBy(delta);
  }

  void GenerateOneFrame() { compositor()->ScheduleFullRedraw(); }

  ui::Compositor* compositor() { return host_->GetCompositor(); }

  aura::Window* root() { return &root_; }
  aura::Window* anim_root() { return anim_root_.get(); }

  void DeleteAnimRoot() {
    if (anim_root_) {
      anim_root_->RemoveChild(&anim_leaf_);
      anim_root_.reset();
    }
  }

 private:
  base::test::TaskEnvironment task_environment_{
      base::test::TaskEnvironment::TimeSource::MOCK_TIME,
      base::test::TaskEnvironment::MainThreadType::UI};

  std::unique_ptr<ui::TestContextFactories> context_factories_;
  std::unique_ptr<ui::TestCompositorHost> host_;
  aura::Window root_{nullptr};

  std::unique_ptr<aura::Window> anim_root_;
  aura::Window anim_leaf_{nullptr};
};

}  // namespace

TEST_F(LayerCopyAnimatorTest, Basic) {
  ui::ScopedAnimationDurationScaleMode non_zero(
      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
  auto* root_layer = root()->layer();
  auto* anim_layer = anim_root()->layer();

  auto* animator = new TestLayerCopyAnimator(anim_root());
  EXPECT_FALSE(animator->animation_requested());
  EXPECT_EQ(2u, anim_layer->children().size());
  EXPECT_EQ(1u, root_layer->children().size());

  GenerateOneFrame();
  auto* copied_layer = animator->WaitForCopy();
  EXPECT_TRUE(copied_layer);

  EXPECT_EQ(ui::LAYER_SOLID_COLOR, copied_layer->type());
  EXPECT_EQ(gfx::Size(100, 100), copied_layer->size());

  ui::TestLayerAnimationObserver observer;
  animator->MaybeStartAnimation(
      &observer, base::BindOnce([](ui::Layer* layer,
                                   ui::LayerAnimationObserver* observer) {
        DCHECK(observer);
        layer->SetOpacity(0.f);
        ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
        settings.SetTransitionDuration(base::Milliseconds(1));
        layer->SetOpacity(1.f);

        ui::LayerAnimationSequence* sequence = new ui::LayerAnimationSequence(
            ui::LayerAnimationElement::CreateOpacityElement(1.0,
                                                            base::TimeDelta()));
        sequence->AddObserver(observer);
        layer->GetAnimator()->ScheduleAnimation(sequence);
      }));
  ASSERT_EQ(2u, root_layer->children().size());
  EXPECT_EQ(copied_layer, root_layer->children()[1]);
  EXPECT_TRUE(copied_layer->GetAnimator()->is_animating());
  EXPECT_EQ(0.f, anim_layer->GetTargetOpacity());

  Advance(base::Milliseconds(1000));
  EXPECT_EQ(3, observer.last_ended_sequence_epoch());
  EXPECT_EQ(1u, root_layer->children().size());
  EXPECT_EQ(1.f, anim_layer->GetTargetOpacity());
}

TEST_F(LayerCopyAnimatorTest, CopyAfterAnimationRequest) {
  ui::ScopedAnimationDurationScaleMode non_zero(
      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
  auto* root_layer = root()->layer();
  auto* anim_layer = anim_root()->layer();

  auto* animator = new TestLayerCopyAnimator(anim_root());
  EXPECT_FALSE(animator->animation_requested());
  EXPECT_EQ(2u, anim_layer->children().size());
  EXPECT_EQ(1u, root_layer->children().size());

  ui::TestLayerAnimationObserver observer;

  animator->MaybeStartAnimation(
      &observer, base::BindOnce([](ui::Layer* layer,
                                   ui::LayerAnimationObserver* observer) {
        DCHECK(observer);
        layer->SetOpacity(0.f);
        ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
        // Longer duration so that animation doesn't end after copy.
        settings.SetTransitionDuration(base::Milliseconds(100));
        layer->SetOpacity(1.f);

        ui::LayerAnimationSequence* sequence = new ui::LayerAnimationSequence(
            ui::LayerAnimationElement::CreateOpacityElement(1.0,
                                                            base::TimeDelta()));
        sequence->AddObserver(observer);
        layer->GetAnimator()->ScheduleAnimation(sequence);
      }));

  EXPECT_FALSE(animator->copied_layer_for_test());

  GenerateOneFrame();
  auto* copied_layer = animator->WaitForCopy();
  ASSERT_TRUE(copied_layer);

  EXPECT_EQ(ui::LAYER_SOLID_COLOR, copied_layer->type());
  EXPECT_EQ(gfx::Size(100, 100), copied_layer->size());
  ASSERT_EQ(2u, root_layer->children().size());
  EXPECT_EQ(copied_layer, root_layer->children()[1]);
  EXPECT_TRUE(copied_layer->GetAnimator()->is_animating());
  EXPECT_EQ(0.f, anim_layer->GetTargetOpacity());

  Advance(base::Milliseconds(1000));

  // When animation starts before copy, it registers the observer to fake
  // sequecne, hence become 6.
  EXPECT_EQ(6, observer.last_ended_sequence_epoch());
  EXPECT_EQ(1u, root_layer->children().size());
  EXPECT_EQ(1.f, anim_layer->GetTargetOpacity());
}

TEST_F(LayerCopyAnimatorTest, CancelByResize) {
  ui::ScopedAnimationDurationScaleMode non_zero(
      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
  auto* root_layer = root()->layer();
  auto* anim_layer = anim_root()->layer();

  auto* animator = new TestLayerCopyAnimator(anim_root());
  EXPECT_FALSE(animator->animation_requested());
  EXPECT_EQ(2u, anim_layer->children().size());
  EXPECT_EQ(1u, root_layer->children().size());

  anim_layer->SetBounds(gfx::Rect(210, 210));
  GenerateOneFrame();
  auto* copied_layer = animator->WaitForCopy();
  ASSERT_FALSE(copied_layer);

  ui::TestLayerAnimationObserver observer;
  EXPECT_EQ(-1, observer.last_aborted_sequence_epoch());
  animator->MaybeStartAnimation(
      &observer, base::BindOnce([](ui::Layer* layer,
                                   ui::LayerAnimationObserver* observer) {
        EXPECT_FALSE(true) << "Callback should not be called";
      }));
  EXPECT_EQ(1.f, anim_layer->GetTargetOpacity());
  EXPECT_EQ(1, observer.last_aborted_sequence_epoch());
}

TEST_F(LayerCopyAnimatorTest, CancelByDelete) {
  ui::ScopedAnimationDurationScaleMode non_zero(
      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
  auto* root_layer = root()->layer();
  auto* anim_layer = anim_root()->layer();

  auto* animator = new LayerCopyAnimator(anim_root());
  EXPECT_FALSE(animator->animation_requested());
  EXPECT_EQ(2u, anim_layer->children().size());
  EXPECT_EQ(1u, root_layer->children().size());

  GenerateOneFrame();
  DeleteAnimRoot();
}

TEST_F(LayerCopyAnimatorTest, CancelByStop) {
  ui::ScopedAnimationDurationScaleMode non_zero(
      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
  auto* root_layer = root()->layer();
  auto* anim_layer = anim_root()->layer();

  auto* animator = new TestLayerCopyAnimator(anim_root());
  EXPECT_FALSE(animator->animation_requested());
  EXPECT_EQ(2u, anim_layer->children().size());
  EXPECT_EQ(1u, root_layer->children().size());

  GenerateOneFrame();
  auto* copied_layer = animator->WaitForCopy();
  ASSERT_TRUE(copied_layer);

  EXPECT_EQ(ui::LAYER_SOLID_COLOR, copied_layer->type());
  EXPECT_EQ(gfx::Size(100, 100), copied_layer->size());

  ui::TestLayerAnimationObserver observer;

  animator->MaybeStartAnimation(
      &observer, base::BindOnce([](ui::Layer* layer,
                                   ui::LayerAnimationObserver* observer) {
        DCHECK(observer);
        layer->SetOpacity(0.f);
        ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
        settings.SetTransitionDuration(base::Milliseconds(100));
        layer->SetOpacity(1.f);

        ui::LayerAnimationSequence* sequence = new ui::LayerAnimationSequence(
            ui::LayerAnimationElement::CreateOpacityElement(1.0,
                                                            base::TimeDelta()));
        sequence->AddObserver(observer);
        layer->GetAnimator()->ScheduleAnimation(sequence);
      }));
  ASSERT_EQ(2u, root_layer->children().size());
  EXPECT_EQ(copied_layer, root_layer->children()[1]);
  EXPECT_TRUE(copied_layer->GetAnimator()->is_animating());
  EXPECT_EQ(0.f, anim_layer->GetTargetOpacity());
  copied_layer->GetAnimator()->StopAnimating();

  Advance(base::Milliseconds(1000));

  EXPECT_EQ(3, observer.last_ended_sequence_epoch());
  EXPECT_EQ(1u, root_layer->children().size());
  EXPECT_EQ(1.f, anim_layer->GetTargetOpacity());
}

TEST_F(LayerCopyAnimatorTest, NoAnimationStopImmediately) {
  ui::ScopedAnimationDurationScaleMode non_zero(
      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
  auto* root_layer = root()->layer();
  auto* anim_layer = anim_root()->layer();

  auto* animator = new TestLayerCopyAnimator(anim_root());
  EXPECT_FALSE(animator->animation_requested());
  EXPECT_EQ(2u, anim_layer->children().size());
  EXPECT_EQ(1u, root_layer->children().size());

  GenerateOneFrame();
  auto* copied_layer = animator->WaitForCopy();
  ASSERT_TRUE(copied_layer);

  EXPECT_EQ(ui::LAYER_SOLID_COLOR, copied_layer->type());
  EXPECT_EQ(gfx::Size(100, 100), copied_layer->size());

  ui::TestLayerAnimationObserver observer;

  animator->MaybeStartAnimation(
      &observer, base::BindOnce([](ui::Layer* layer,
                                   ui::LayerAnimationObserver* observer) {}));
  EXPECT_EQ(1u, root_layer->children().size());
  EXPECT_EQ(1.f, anim_layer->GetTargetOpacity());
  EXPECT_EQ(1, observer.last_ended_sequence_epoch());
}

}  //  namespace ash