chromium/ash/utility/occlusion_tracker_pauser_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/occlusion_tracker_pauser.h"

#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "ui/aura/env.h"
#include "ui/aura/window_occlusion_tracker.h"
#include "ui/aura/window_tree_host.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/test/begin_main_frame_waiter.h"

namespace ash {
namespace {

class TestObserver final : public ui::CompositorAnimationObserver {
 public:
  TestObserver() = default;
  TestObserver(const TestObserver&) = delete;
  TestObserver& operator=(const TestObserver&) = delete;
  ~TestObserver() override = default;

  // ui::CompositorAnimationObserver:
  void OnAnimationStep(base::TimeTicks timestamp) override {}
  void OnCompositingShuttingDown(ui::Compositor* compositor) override {}
};

}  // namespace

class OcclusionTrackerPauserTest : public AshTestBase {
 public:
  OcclusionTrackerPauserTest()
      : AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
  ~OcclusionTrackerPauserTest() override = default;
};

TEST_F(OcclusionTrackerPauserTest, Basic) {
  aura::WindowOcclusionTracker* tracker =
      aura::Env::GetInstance()->GetWindowOcclusionTracker();

  ASSERT_FALSE(tracker->IsPaused());
  Shell::Get()->occlusion_tracker_pauser()->PauseUntilAnimationsEnd(
      base::TimeDelta());
  EXPECT_TRUE(tracker->IsPaused());

  auto* compositor = Shell::GetPrimaryRootWindow()->GetHost()->compositor();

  TestObserver observer1, observer2;

  compositor->AddAnimationObserver(&observer1);
  EXPECT_TRUE(tracker->IsPaused());
  compositor->RemoveAnimationObserver(&observer1);

  // Wait for BeginFrame since compositor animation notifications happen
  // on BeginFrame.
  ui::BeginMainFrameWaiter(compositor).Wait();
  EXPECT_FALSE(tracker->IsPaused());

  compositor->AddAnimationObserver(&observer1);
  EXPECT_FALSE(tracker->IsPaused());
  Shell::Get()->occlusion_tracker_pauser()->PauseUntilAnimationsEnd(
      base::TimeDelta());
  EXPECT_TRUE(tracker->IsPaused());
  compositor->AddAnimationObserver(&observer2);
  EXPECT_TRUE(tracker->IsPaused());
  compositor->RemoveAnimationObserver(&observer2);
  EXPECT_TRUE(tracker->IsPaused());
  compositor->RemoveAnimationObserver(&observer1);

  // Wait for BeginFrame since compositor animation notifications happen
  // on BeginFrame.
  ui::BeginMainFrameWaiter(compositor).Wait();
  EXPECT_FALSE(tracker->IsPaused());
}

TEST_F(OcclusionTrackerPauserTest, MultiDisplay) {
  aura::WindowOcclusionTracker* tracker =
      aura::Env::GetInstance()->GetWindowOcclusionTracker();
  UpdateDisplay("800x1000, 800x1000");

  auto* compositor1 = Shell::GetAllRootWindows()[0]->GetHost()->compositor();
  auto* compositor2 = Shell::GetAllRootWindows()[1]->GetHost()->compositor();

  TestObserver observer1, observer2;

  Shell::Get()->occlusion_tracker_pauser()->PauseUntilAnimationsEnd(
      base::TimeDelta());
  EXPECT_TRUE(tracker->IsPaused());
  compositor1->AddAnimationObserver(&observer1);
  compositor2->AddAnimationObserver(&observer2);
  EXPECT_TRUE(tracker->IsPaused());
  compositor1->RemoveAnimationObserver(&observer1);

  // Wait for BeginFrame since compositor animation notifications happen
  // on BeginFrame.
  ui::BeginMainFrameWaiter(compositor1).Wait();

  // Tracker should still be paused.
  EXPECT_TRUE(tracker->IsPaused());

  compositor2->RemoveAnimationObserver(&observer2);

  // Wait for BeginFrame since compositor animation notifications happen
  // on BeginFrame.
  ui::BeginMainFrameWaiter(compositor2).Wait();
  EXPECT_FALSE(tracker->IsPaused());

  // Disconnect display.
  Shell::Get()->occlusion_tracker_pauser()->PauseUntilAnimationsEnd(
      base::TimeDelta());
  EXPECT_TRUE(tracker->IsPaused());
  compositor1->AddAnimationObserver(&observer1);
  compositor2->AddAnimationObserver(&observer2);
  EXPECT_TRUE(tracker->IsPaused());

  // Wait for BeginFrame since compositor animation notifications happen
  // on BeginFrame.
  ui::BeginMainFrameWaiter(compositor1).Wait();
  // Tracker should still be paused.
  EXPECT_TRUE(tracker->IsPaused());

  compositor1->RemoveAnimationObserver(&observer1);
  EXPECT_TRUE(tracker->IsPaused());

  // Wait for BeginFrame since compositor animation notifications happen
  // on BeginFrame.
  ui::BeginMainFrameWaiter(compositor1).Wait();
  EXPECT_TRUE(tracker->IsPaused());

  UpdateDisplay("800x1000");
  EXPECT_TRUE(tracker->IsPaused());

  // Wait for BeginFrame since compositor animation notifications happen
  // on BeginFrame.
  ui::BeginMainFrameWaiter(compositor1).Wait();
  EXPECT_FALSE(tracker->IsPaused());
}

TEST_F(OcclusionTrackerPauserTest, MultiDisplaySingleAnimatingCompositor) {
  aura::WindowOcclusionTracker* tracker =
      aura::Env::GetInstance()->GetWindowOcclusionTracker();
  UpdateDisplay("800x1000, 800x1000");

  auto* compositor1 = Shell::GetAllRootWindows()[0]->GetHost()->compositor();
  TestObserver observer1;

  Shell::Get()->occlusion_tracker_pauser()->PauseUntilAnimationsEnd(
      base::TimeDelta());
  EXPECT_TRUE(tracker->IsPaused());
  compositor1->AddAnimationObserver(&observer1);
  EXPECT_TRUE(tracker->IsPaused());

  // Wait for BeginFrame since compositor animation notifications happen
  // on BeginFrame.
  ui::BeginMainFrameWaiter(compositor1).Wait();
  EXPECT_TRUE(tracker->IsPaused());

  compositor1->RemoveAnimationObserver(&observer1);
  EXPECT_TRUE(tracker->IsPaused());

  // Wait for BeginFrame since compositor animation notifications happen
  // on BeginFrame.
  ui::BeginMainFrameWaiter(compositor1).Wait();

  // There are no more animations, so expect the tracker to be unpaused.
  EXPECT_FALSE(tracker->IsPaused());
}

TEST_F(OcclusionTrackerPauserTest, Timeout) {
  aura::WindowOcclusionTracker* tracker =
      aura::Env::GetInstance()->GetWindowOcclusionTracker();
  UpdateDisplay("800x1000, 800x1000");

  auto* compositor1 = Shell::GetAllRootWindows()[0]->GetHost()->compositor();
  auto* compositor2 = Shell::GetAllRootWindows()[1]->GetHost()->compositor();

  // Add observer to emulate animations start/end.
  TestObserver observer1, observer2;

  Shell::Get()->occlusion_tracker_pauser()->PauseUntilAnimationsEnd(
      base::Seconds(2));
  EXPECT_TRUE(tracker->IsPaused());
  compositor1->AddAnimationObserver(&observer1);
  compositor2->AddAnimationObserver(&observer2);
  EXPECT_TRUE(tracker->IsPaused());

  // Wait for BeginFrame since compositor animation notifications happen
  // on BeginFrame.
  ui::BeginMainFrameWaiter waiter1(compositor1);
  ui::BeginMainFrameWaiter waiter2(compositor2);
  waiter1.Wait();
  waiter2.Wait();

  task_environment()->FastForwardBy(base::Seconds(1));
  EXPECT_TRUE(tracker->IsPaused());
  task_environment()->FastForwardBy(base::Seconds(2));

  EXPECT_FALSE(tracker->IsPaused());
  compositor1->RemoveAnimationObserver(&observer1);
  compositor2->RemoveAnimationObserver(&observer2);
}

}  //  namespace ash