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

#include <stddef.h>

#include <unordered_map>
#include <vector>

#include "base/containers/flat_set.h"
#include "base/test/test_mock_time_task_runner.h"
#include "components/viz/common/surfaces/surface_id.h"
#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
#include "components/viz/service/surfaces/surface.h"
#include "components/viz/service/surfaces/surface_manager.h"
#include "components/viz/test/compositor_frame_helpers.h"
#include "components/viz/test/surface_id_allocator_set.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::ElementsAre;
using testing::IsEmpty;
using testing::SizeIs;
using testing::UnorderedElementsAre;

namespace viz {
namespace {

constexpr FrameSinkId kFrameSink1(1, 0);
constexpr FrameSinkId kFrameSink2(2, 0);
constexpr FrameSinkId kFrameSink3(3, 0);

}  // namespace

// Tests for reference tracking in CompositorFrameSinkSupport and
// SurfaceManager.
class SurfaceReferencesTest : public testing::Test {
 public:
  SurfaceReferencesTest()
      : task_runner_(base::MakeRefCounted<base::TestMockTimeTaskRunner>()),
        scoped_context_(task_runner_.get()) {}

  SurfaceManager& GetSurfaceManager() { return *manager_->surface_manager(); }

  // Creates a new Surface with the provided |frame_sink_id| and |local_id|.
  // Will first create a Surfacesupport for |frame_sink_id| if necessary.
  SurfaceId CreateSurface(const FrameSinkId& frame_sink_id,
                          uint32_t parent_id) {
    SurfaceId surface_id =
        allocator_set_.MakeSurfaceId(frame_sink_id, parent_id);
    GetCompositorFrameSinkSupport(frame_sink_id)
        .SubmitCompositorFrame(surface_id.local_surface_id(),
                               MakeDefaultCompositorFrame());
    return surface_id;
  }

  // Destroy Surface with |surface_id|.
  void DestroySurface(const SurfaceId& surface_id) {
    GetCompositorFrameSinkSupport(surface_id.frame_sink_id())
        .EvictSurface(surface_id.local_surface_id());
  }

  CompositorFrameSinkSupport& GetCompositorFrameSinkSupport(
      const FrameSinkId& frame_sink_id) {
    auto& support_ptr = supports_[frame_sink_id];
    if (!support_ptr) {
      manager_->RegisterFrameSinkId(frame_sink_id,
                                    true /* report_activation */);
      constexpr bool is_root = false;
      support_ptr = std::make_unique<CompositorFrameSinkSupport>(
          nullptr, manager_.get(), frame_sink_id, is_root);
    }
    return *support_ptr;
  }

  void DestroyCompositorFrameSinkSupport(const FrameSinkId& frame_sink_id) {
    auto support_ptr = supports_.find(frame_sink_id);
    ASSERT_NE(support_ptr, supports_.end());
    supports_.erase(support_ptr);
    manager_->InvalidateFrameSinkId(frame_sink_id, {});
  }

  void RemoveSurfaceReference(const SurfaceId& parent_id,
                              const SurfaceId& child_id) {
    manager_->surface_manager()->RemoveSurfaceReferences(
        {SurfaceReference(parent_id, child_id)});
  }

  void AddSurfaceReference(const SurfaceId& parent_id,
                           const SurfaceId& child_id) {
    manager_->surface_manager()->AddSurfaceReferences(
        {SurfaceReference(parent_id, child_id)});
  }

  // Returns all the references where |surface_id| is the parent.
  const base::flat_set<SurfaceId>& GetReferencesFrom(
      const SurfaceId& surface_id) {
    return GetSurfaceManager().GetSurfacesReferencedByParent(surface_id);
  }

  // Returns all the references where |surface_id| is the child.
  const base::flat_set<SurfaceId> GetReferencesFor(
      const SurfaceId& surface_id) {
    return GetSurfaceManager().GetSurfacesThatReferenceChildForTesting(
        surface_id);
  }

  // Temporary references are stored as a map in SurfaceManager. This method
  // converts the map to a vector.
  std::vector<SurfaceId> GetAllTempReferences() {
    std::vector<SurfaceId> temp_references;
    for (auto& map_entry : GetSurfaceManager().temporary_references_)
      temp_references.push_back(map_entry.first);
    return temp_references;
  }

  bool IsTemporaryReferenceTimerRunning() const {
    return manager_->surface_manager()->expire_timer_->IsRunning();
  }

 protected:
  // testing::Test:
  void SetUp() override {
    // Start each test with a fresh SurfaceManager instance.
    manager_ = std::make_unique<FrameSinkManagerImpl>(
        FrameSinkManagerImpl::InitParams());
  }
  void TearDown() override {
    supports_.clear();
    manager_.reset();
  }

  // Use a TestMockTimeTaskRunner so we can test timer behaviour in |manager_|.
  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
  base::TestMockTimeTaskRunner::ScopedContext scoped_context_;

  std::unique_ptr<FrameSinkManagerImpl> manager_;
  std::unordered_map<FrameSinkId,
                     std::unique_ptr<CompositorFrameSinkSupport>,
                     FrameSinkIdHash>
      supports_;
  SurfaceIdAllocatorSet allocator_set_;
};

TEST_F(SurfaceReferencesTest, AddReference) {
  SurfaceId id1 = CreateSurface(kFrameSink1, 1);
  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);

  EXPECT_THAT(GetReferencesFor(id1),
              UnorderedElementsAre(GetSurfaceManager().GetRootSurfaceId()));
  EXPECT_THAT(GetReferencesFrom(id1), IsEmpty());
}

TEST_F(SurfaceReferencesTest, AddRemoveReference) {
  SurfaceId id1 = CreateSurface(kFrameSink1, 1);
  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);

  SurfaceId id2 = CreateSurface(kFrameSink2, 1);
  AddSurfaceReference(id1, id2);

  EXPECT_THAT(GetReferencesFor(id1),
              UnorderedElementsAre(GetSurfaceManager().GetRootSurfaceId()));
  EXPECT_THAT(GetReferencesFor(id2), UnorderedElementsAre(id1));
  EXPECT_THAT(GetReferencesFrom(id1), UnorderedElementsAre(id2));
  EXPECT_THAT(GetReferencesFrom(id2), IsEmpty());

  RemoveSurfaceReference(id1, id2);
  EXPECT_THAT(GetReferencesFor(id1), SizeIs(1));
  EXPECT_THAT(GetReferencesFor(id2), IsEmpty());
  EXPECT_THAT(GetReferencesFrom(id1), IsEmpty());
  EXPECT_THAT(GetReferencesFrom(id2), IsEmpty());
}

TEST_F(SurfaceReferencesTest, NewSurfaceFromFrameSink) {
  SurfaceId id1 = CreateSurface(kFrameSink1, 1);
  SurfaceId id2 = CreateSurface(kFrameSink2, 1);
  SurfaceId id3 = CreateSurface(kFrameSink3, 1);

  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
  AddSurfaceReference(id1, id2);
  AddSurfaceReference(id2, id3);

  // |kFramesink2| received a CompositorFrame with a new size, so it destroys
  // |id2| and creates |id2_next|. No reference have been removed yet.
  SurfaceId id2_next = CreateSurface(kFrameSink2, 2);
  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2_next));

  // Add references to and from |id2_next|.
  AddSurfaceReference(id1, id2_next);
  AddSurfaceReference(id2_next, id3);
  EXPECT_THAT(GetReferencesFor(id2), UnorderedElementsAre(id1));
  EXPECT_THAT(GetReferencesFor(id2_next), UnorderedElementsAre(id1));
  EXPECT_THAT(GetReferencesFor(id3), UnorderedElementsAre(id2, id2_next));

  RemoveSurfaceReference(id1, id2);
  GetSurfaceManager().GarbageCollectSurfaces();
  EXPECT_THAT(GetReferencesFor(id2), IsEmpty());
  EXPECT_THAT(GetReferencesFor(id2_next), UnorderedElementsAre(id1));
  EXPECT_THAT(GetReferencesFor(id3), UnorderedElementsAre(id2_next));

  // |id2| should be deleted during GC but other surfaces shouldn't.
  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2_next));
  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id3));
}

TEST_F(SurfaceReferencesTest, ReferenceCycleGetsDeleted) {
  SurfaceId id1 = CreateSurface(kFrameSink1, 1);
  SurfaceId id2 = CreateSurface(kFrameSink2, 1);
  SurfaceId id3 = CreateSurface(kFrameSink3, 1);

  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
  AddSurfaceReference(id1, id2);
  AddSurfaceReference(id2, id3);

  // This reference forms a cycle between id2 and id3.
  AddSurfaceReference(id3, id2);

  DestroySurface(id3);
  DestroySurface(id2);
  DestroySurface(id1);

  RemoveSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);

  GetSurfaceManager().GarbageCollectSurfaces();

  // Removing the reference from the root to id1 should allow all three surfaces
  // to be deleted during GC even with a cycle between 2 and 3.
  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id3));
}

TEST_F(SurfaceReferencesTest, SurfacesAreDeletedDuringGarbageCollection) {
  SurfaceId id1 = CreateSurface(kFrameSink1, 1);
  SurfaceId id2 = CreateSurface(kFrameSink2, 1);

  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
  AddSurfaceReference(id1, id2);

  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));

  // Destroying the surfaces shouldn't delete them yet, since there is still an
  // active reference on all surfaces.
  DestroySurface(id1);
  DestroySurface(id2);
  GetSurfaceManager().GarbageCollectSurfaces();

  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));

  // Should delete |id2| when the only reference to it is removed.
  RemoveSurfaceReference(id1, id2);
  GetSurfaceManager().GarbageCollectSurfaces();
  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id2));

  // Should delete |id1| when the only reference to it is removed.
  RemoveSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
  GetSurfaceManager().GarbageCollectSurfaces();
  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
}

TEST_F(SurfaceReferencesTest, GarbageCollectionWorksRecursively) {
  SurfaceId id1 = CreateSurface(kFrameSink1, 1);
  SurfaceId id2 = CreateSurface(kFrameSink2, 1);
  SurfaceId id3 = CreateSurface(kFrameSink3, 1);

  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
  AddSurfaceReference(id1, id2);
  AddSurfaceReference(id2, id3);

  DestroySurface(id3);
  DestroySurface(id2);
  DestroySurface(id1);

  GetSurfaceManager().GarbageCollectSurfaces();

  // Destroying the surfaces shouldn't delete them yet, since there is still an
  // active reference on all surfaces.
  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id3));
  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id1));

  RemoveSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);

  GetSurfaceManager().GarbageCollectSurfaces();

  // Removing the reference from the root to id1 should allow all three surfaces
  // to be deleted during GC.
  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id3));
}

// Verify that surfaces marked as live are not garbage collected and any
// dependencies are also not garbage collected.
TEST_F(SurfaceReferencesTest, LiveSurfaceStillReachable) {
  SurfaceId id1 = CreateSurface(kFrameSink1, 1);
  SurfaceId id2 = CreateSurface(kFrameSink2, 1);
  SurfaceId id3 = CreateSurface(kFrameSink3, 1);

  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
  AddSurfaceReference(id1, id2);
  AddSurfaceReference(id2, id3);
  ASSERT_THAT(GetAllTempReferences(), IsEmpty());

  // Marking |id3| for destruction shouldn't cause it be garbage collected
  // because |id2| is still reachable from the root.
  DestroySurface(id3);
  GetSurfaceManager().GarbageCollectSurfaces();
  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id3));

  // Removing the surface reference to |id2| makes it not reachable from the
  // root, however it's not marked as destroyed and is still live. Make sure we
  // also don't delete any dependencies, such as |id3|, as well.
  RemoveSurfaceReference(id1, id2);
  GetSurfaceManager().GarbageCollectSurfaces();
  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id3));
  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));

  // |id2| is unreachable and destroyed. Garbage collection should delete both
  // |id2| and |id3| now.
  DestroySurface(id2);
  GetSurfaceManager().GarbageCollectSurfaces();
  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id3));
  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
}

TEST_F(SurfaceReferencesTest, TryAddReferenceSameReferenceTwice) {
  SurfaceId id1 = CreateSurface(kFrameSink1, 1);
  SurfaceId id2 = CreateSurface(kFrameSink2, 1);

  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
  AddSurfaceReference(id1, id2);
  EXPECT_THAT(GetReferencesFor(id2), SizeIs(1));
  EXPECT_THAT(GetReferencesFrom(id1), SizeIs(1));

  // The second request should be ignored without crashing.
  AddSurfaceReference(id1, id2);
  EXPECT_THAT(GetReferencesFor(id2), SizeIs(1));
  EXPECT_THAT(GetReferencesFrom(id1), SizeIs(1));
}

TEST_F(SurfaceReferencesTest, AddingSelfReferenceFails) {
  SurfaceId id1 = CreateSurface(kFrameSink2, 1);

  // A temporary reference must exist to |id1|.
  EXPECT_THAT(GetAllTempReferences(), ElementsAre(id1));
  EXPECT_THAT(GetReferencesFrom(id1), IsEmpty());
  EXPECT_THAT(GetReferencesFor(id1), IsEmpty());

  // Try to add a self reference. This should fail.
  AddSurfaceReference(id1, id1);

  // Adding a self reference should be ignored without crashing. The temporary
  // reference to |id1| must still exist.
  EXPECT_THAT(GetAllTempReferences(), ElementsAre(id1));
  EXPECT_THAT(GetReferencesFrom(id1), IsEmpty());
  EXPECT_THAT(GetReferencesFor(id1), IsEmpty());
}

TEST_F(SurfaceReferencesTest, RemovingNonexistantReferenceFails) {
  SurfaceId id1 = CreateSurface(kFrameSink1, 1);
  SurfaceId id2 = CreateSurface(kFrameSink2, 1);

  // Removing non-existent reference should be ignored.
  AddSurfaceReference(id1, id2);
  RemoveSurfaceReference(id2, id1);
  EXPECT_THAT(GetReferencesFrom(id1), SizeIs(1));
  EXPECT_THAT(GetReferencesFor(id2), SizeIs(1));
}

TEST_F(SurfaceReferencesTest, AddSurfaceThenReference) {
  // Create a new surface.
  const SurfaceId surface_id = CreateSurface(kFrameSink2, 1);

  // A temporary reference must be added to |surface_id|.
  EXPECT_THAT(GetAllTempReferences(), ElementsAre(surface_id));

  // Create |parent_id| and add a real reference from it to |surface_id|.
  const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
  AddSurfaceReference(parent_id, surface_id);

  // The temporary reference to |surface_id| should be gone.
  // The only temporary reference should be to |parent_id|.
  // There must be a real reference from |parent_id| to |child_id|.
  EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id));
  EXPECT_THAT(GetAllTempReferences(), ElementsAre(parent_id));
}

TEST_F(SurfaceReferencesTest, AddSurfaceThenRootReference) {
  // Create a new surface.
  const SurfaceId surface_id = CreateSurface(kFrameSink1, 1);

  // Temporary reference should be added to |surface_id|.
  EXPECT_THAT(GetAllTempReferences(), ElementsAre(surface_id));

  // Add a surface reference from root to |surface_id|.
  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), surface_id);

  // The temporary reference should be gone and there should now be a surface
  // reference from root to |surface_id|.
  EXPECT_THAT(GetAllTempReferences(), IsEmpty());
  EXPECT_THAT(GetReferencesFrom(GetSurfaceManager().GetRootSurfaceId()),
              ElementsAre(surface_id));
}

TEST_F(SurfaceReferencesTest, AddTwoSurfacesThenOneReference) {
  // Create two surfaces with different FrameSinkIds.
  const SurfaceId surface_id1 = CreateSurface(kFrameSink2, 1);
  const SurfaceId surface_id2 = CreateSurface(kFrameSink3, 1);

  // Temporary reference should be added for both surfaces.
  EXPECT_THAT(GetAllTempReferences(),
              UnorderedElementsAre(surface_id1, surface_id2));

  // Create |parent_id| and add a real reference from it to |surface_id1|.
  const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
  AddSurfaceReference(parent_id, surface_id1);

  // Real reference must be added to |surface_id1| and the temporary reference
  // to it must be gone.
  // There should still be a temporary reference left to |surface_id2|.
  // A temporary reference to |parent_id| must be created.
  EXPECT_THAT(GetAllTempReferences(),
              UnorderedElementsAre(parent_id, surface_id2));
  EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id1));
}

TEST_F(SurfaceReferencesTest, AddSurfacesSkipReference) {
  // Add two surfaces that have the same FrameSinkId. This would happen
  // when a client submits two CompositorFrames before parent submits a new
  // CompositorFrame.
  const SurfaceId surface_id1 = CreateSurface(kFrameSink2, 1);
  const SurfaceId surface_id2 = CreateSurface(kFrameSink2, 2);

  // Temporary references should be added for both surfaces and they should be
  // stored in the order of creation.
  EXPECT_THAT(GetAllTempReferences(),
              UnorderedElementsAre(surface_id1, surface_id2));

  // Create |parent_id| and add a reference from it to |surface_id2| which was
  // created later.
  const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
  AddSurfaceReference(parent_id, surface_id2);

  // The real reference should be added for |surface_id2| and the temporary
  // references to both |surface_id1| and |surface_id2| should be gone.
  // There should be a temporary reference to |parent_id|.
  EXPECT_THAT(GetAllTempReferences(), ElementsAre(parent_id));
  EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id2));
}

TEST_F(SurfaceReferencesTest, RemoveFirstTempReferenceOnly) {
  // Add two surfaces that have the same FrameSinkId. This would happen
  // when a client submits two CFs before parent submits a new CF.
  const SurfaceId surface_id1 = CreateSurface(kFrameSink2, 1);
  const SurfaceId surface_id2 = CreateSurface(kFrameSink2, 2);

  // Temporary references should be added for both surfaces and they should be
  // stored in the order of creation.
  EXPECT_THAT(GetAllTempReferences(),
              UnorderedElementsAre(surface_id1, surface_id2));

  // Create |parent_id| and add a reference from it to |surface_id1| which was
  // created earlier.
  const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
  AddSurfaceReference(parent_id, surface_id1);

  // The real reference should be added for |surface_id1| and its temporary
  // reference should be removed. The temporary reference for |surface_id2|
  // should remain. A temporary reference must be added for |parent_id|.
  EXPECT_THAT(GetAllTempReferences(),
              UnorderedElementsAre(parent_id, surface_id2));
  EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id1));
}

TEST_F(SurfaceReferencesTest, SurfaceWithTemporaryReferenceIsDeleted) {
  const SurfaceId id1 = CreateSurface(kFrameSink1, 1);
  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);

  // We create |id2| and never add a real reference to it. This leaves the
  // temporary reference.
  const SurfaceId id2 = CreateSurface(kFrameSink2, 1);
  ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id2));
  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));

  // Destroy both surfaces so they can be garbage collected. We remove the
  // surface reference to |id1| which will run GarbageCollectSurfaces().
  DestroySurface(id1);
  DestroySurface(id2);
  RemoveSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);

  GetSurfaceManager().GarbageCollectSurfaces();

  // |id1| is destroyed and has no references, so it's deleted.
  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id1));

  // |id2| is destroyed and the temporary reference is dropped, so it's deleted.
  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
}

// Checks that adding a surface reference clears the temporary reference.
TEST_F(SurfaceReferencesTest, InvalidateHasNoEffectOnSurfaceReferences) {
  const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), parent_id);

  const SurfaceId id1 = CreateSurface(kFrameSink2, 1);
  ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1));

  // Adding a real surface reference will remove the temporary reference.
  AddSurfaceReference(parent_id, id1);
  ASSERT_THAT(GetAllTempReferences(), IsEmpty());
  ASSERT_THAT(GetReferencesFor(id1), UnorderedElementsAre(parent_id));
}

// Check that old temporary references are deleted, but only for surfaces marked
// as destroyed.
TEST_F(SurfaceReferencesTest, MarkOldTemporaryReferences) {
  constexpr base::TimeDelta kFastForwardTime = base::Seconds(30);

  // There are no temporary references so the timer should be stopped.
  EXPECT_FALSE(IsTemporaryReferenceTimerRunning());

  // Creating the surface should create a temporary reference and start timer.
  const SurfaceId id = CreateSurface(kFrameSink2, 1);
  ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id));
  EXPECT_TRUE(IsTemporaryReferenceTimerRunning());

  // The temporary reference should not be marked as old and then deleted
  // because the surface is still alive.
  task_runner_->FastForwardBy(kFastForwardTime);
  ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id));
  EXPECT_TRUE(IsTemporaryReferenceTimerRunning());

  // After the surface is marked as destroyed, the temporary reference should
  // be marked as old then deleted. The timer should stop because there are no
  // temporary references.
  DestroySurface(id);
  task_runner_->FastForwardBy(kFastForwardTime);
  ASSERT_THAT(GetAllTempReferences(), IsEmpty());
  EXPECT_FALSE(IsTemporaryReferenceTimerRunning());
}

}  // namespace viz
