chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc

// 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 <tuple>

#include "base/containers/contains.h"
#include "base/run_loop.h"
#include "base/test/run_until.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_trace_processor.h"
#include "components/input/features.h"
#include "components/viz/common/constants.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
#include "components/viz/service/input/mock_input_manager.h"
#include "components/viz/service/surfaces/surface.h"
#include "components/viz/service/surfaces/surface_manager.h"
#include "components/viz/test/begin_frame_source_test.h"
#include "components/viz/test/compositor_frame_helpers.h"
#include "components/viz/test/fake_external_begin_frame_source.h"
#include "components/viz/test/fake_surface_observer.h"
#include "components/viz/test/mock_compositor_frame_sink_client.h"
#include "components/viz/test/mock_display_client.h"
#include "components/viz/test/test_output_surface_provider.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/perfetto/include/perfetto/tracing/tracing.h"

namespace viz {
namespace {

constexpr FrameSinkId kFrameSinkIdRoot(1, 1);
constexpr FrameSinkId kFrameSinkIdA(2, 1);
constexpr FrameSinkId kFrameSinkIdB(3, 1);
constexpr FrameSinkId kFrameSinkIdC(4, 1);
constexpr FrameSinkId kFrameSinkIdD(5, 1);
constexpr FrameSinkId kFrameSinkIdE(6, 1);
constexpr FrameSinkId kFrameSinkIdF(7, 1);

// Holds the four interface objects needed to create a RootCompositorFrameSink.
struct RootCompositorFrameSinkData {};

}  // namespace

class FrameSinkManagerTest : public testing::Test {};

TEST_F(FrameSinkManagerTest, CreateRootCompositorFrameSink) {}

TEST_F(FrameSinkManagerTest, InputManagerCreation) {}

TEST_F(FrameSinkManagerTest, CreateCompositorFrameSink) {}

TEST_F(FrameSinkManagerTest, CompositorFrameSinkConnectionLost) {}

TEST_F(FrameSinkManagerTest, SingleClients) {}

// This test verifies that a client is still connected to the BeginFrameSource
// after restart.
TEST_F(FrameSinkManagerTest, ClientRestart) {}

TEST_F(FrameSinkManagerTest, MultipleDisplays) {}

// This test verifies that a BeginFrameSource path to the root from a
// FrameSinkId is preserved even if that FrameSinkId has no children
// and does not have a corresponding CompositorFrameSinkSupport.
TEST_F(FrameSinkManagerTest, ParentWithoutClientRetained) {}

// This test sets up the same hierarchy as ParentWithoutClientRetained.
// However, this unit test registers the BeginFrameSource AFTER C
// has been attached to A. This test verifies that the BeginFrameSource
// propagates all the way to C.
TEST_F(FrameSinkManagerTest,
       ParentWithoutClientRetained_LateBeginFrameRegistration) {}

// Verifies that the SurfaceIds passed to EvictSurfaces will be destroyed in the
// next garbage collection.
TEST_F(FrameSinkManagerTest, EvictSurfaces) {}

// Verify that setting debug label works and that debug labels are cleared when
// FrameSinkId is invalidated.
TEST_F(FrameSinkManagerTest, DebugLabel) {}

// Verifies the the begin frames are throttled properly for the requested frame
// sinks and their children.
TEST_F(FrameSinkManagerTest, Throttle) {}

TEST_F(FrameSinkManagerTest, GlobalThrottle) {}

// Verifies if a frame sink is being captured, it should not be throttled.
TEST_F(FrameSinkManagerTest, NoThrottleOnFrameSinksBeingCaptured) {}

// Verifies if throttling on frame sinks is updated properly when hierarchy
// changes.
TEST_F(FrameSinkManagerTest, ThrottleUponHierarchyChange) {}

TEST_F(FrameSinkManagerTest, EvictRootSurfaceId) {}

TEST_F(FrameSinkManagerTest, EvictNewerRootSurfaceId) {}

TEST_F(FrameSinkManagerTest, SubmitCompositorFrameWithEvictedSurfaceId) {}

// Test that `FrameSinkManagerImpl::DiscardPendingCopyOfOutputRequests`
// relocates the exact `PendingCopyOutputRequest`s to the target surfaces.
TEST_F(FrameSinkManagerTest,
       CopyOutputRequestPreservedAfterDiscardPendingCopyOfOutputRequests) {}

// Submit an exact copy request while there is no frame sink. Such request can
// only be picked up by the specified surface.
TEST_F(FrameSinkManagerTest, ExactCopyOutputRequestTakenBySurfaceRightAway) {}

// Submit an exact copy request while there is no specified surface. Such
// request will be queued in the `CompositorFrameSinkSupport`, just like the
// non-exact requests.
TEST_F(FrameSinkManagerTest,
       ExactCopyOutputRequestQueuedInCompositorFrameSinkSupport) {}

#if BUILDFLAG(IS_ANDROID)
class AndroidFrameSinkManagerTest : public FrameSinkManagerTest,
                                    public ::testing::WithParamInterface<bool> {
 public:
  AndroidFrameSinkManagerTest() {
    scoped_feature_list_.InitWithFeatureState(input::features::kInputOnViz,
                                              /* enabled= */ GetParam());
  }

  bool ExpectedInputManagerCreation() { return input::TransferInputToViz(); }

 private:
  base::test::TracingEnvironment tracing_environment_;
};

TEST_P(AndroidFrameSinkManagerTest, InputManagerCreation) {
  EXPECT_EQ(InputManagerExists(), ExpectedInputManagerCreation());
}

TEST_P(AndroidFrameSinkManagerTest, RenderInputRouterLifecycle) {
  EXPECT_EQ(InputManagerExists(), ExpectedInputManagerCreation());

  base::test::TestTraceProcessor ttp;
  ttp.StartTrace("viz");

  manager_.RegisterFrameSinkId(kFrameSinkIdA, true /* report_activation */);

  // Create a CompositorFrameSinkImpl.
  MockCompositorFrameSinkClient compositor_frame_sink_client;
  mojo::Remote<mojom::CompositorFrameSink> compositor_frame_sink;
  mojo::PendingRemote<blink::mojom::RenderInputRouterClient> rir_client;
  auto config = input::mojom::RenderInputRouterConfig::New();
  config->rir_client = std::move(rir_client);

  manager_.CreateCompositorFrameSink(
      kFrameSinkIdA, /*bundle_id=*/std::nullopt,
      compositor_frame_sink.BindNewPipeAndPassReceiver(),
      compositor_frame_sink_client.BindInterfaceRemote(), std::move(config));
  EXPECT_TRUE(CompositorFrameSinkExists(kFrameSinkIdA));
  if (InputManagerExists()) {
    EXPECT_TRUE(GetMockInputManager()->RIRExistsForFrameSinkId(kFrameSinkIdA));
  }

  // Invalidating should destroy the CompositorFrameSinkImpl.
  manager_.InvalidateFrameSinkId(kFrameSinkIdA);
  EXPECT_FALSE(CompositorFrameSinkExists(kFrameSinkIdA));

  if (InputManagerExists()) {
    EXPECT_FALSE(GetMockInputManager()->RIRExistsForFrameSinkId(kFrameSinkIdA));
  }

  absl::Status status = ttp.StopAndParseTrace();
  EXPECT_TRUE(status.ok()) << status.message();

  std::string query = R"(
    SELECT COUNT(*) AS cnt,
    EXTRACT_ARG(arg_set_id, 'debug.config_is_null') as config_is_null,
    EXTRACT_ARG(arg_set_id, 'debug.frame_sink_id.frame_sink_client_id')
      as client_id,
    EXTRACT_ARG(arg_set_id, 'debug.frame_sink_id.frame_sink_id') as sink_id
    FROM slice
    WHERE slice.name = 'InputManager::OnCreateCompositorFrameSink'
  )";

  auto result = ttp.RunQuery(query);
  EXPECT_TRUE(result.has_value());

  // `result.value()` would look something like this: {{"cnt", "config_is_null",
  // "client_id", "sink_id"}, {"<num>", "<boolean>" "<clientId>", "<sinkId>"}}.
  EXPECT_EQ(result.value().size(), 2u);
  EXPECT_EQ(result.value()[1].size(), 4u);
  if (input::TransferInputToViz()) {
    // Checks if `InputManger::OnCreateCompositorFrameSink` was called for
    // kFrameSinkIdA.
    EXPECT_THAT(
        result.value(),
        testing::ElementsAre(
            testing::ElementsAre("cnt", "config_is_null", "client_id",
                                 "sink_id"),
            testing::ElementsAre(
                "1", "0", base::NumberToString(kFrameSinkIdA.client_id()),
                base::NumberToString(kFrameSinkIdA.sink_id()))));
  } else {
    EXPECT_EQ(result.value()[1][0], "0");
  }

  std::string query2 = R"(
    SELECT COUNT(*) AS cnt,
    EXTRACT_ARG(arg_set_id, 'debug.frame_sink_id.frame_sink_client_id')
      as client_id,
    EXTRACT_ARG(arg_set_id, 'debug.frame_sink_id.frame_sink_id') as sink_id
    FROM slice
    WHERE slice.name = 'InputManager::OnDestroyedCompositorFrameSink'
  )";

  auto result2 = ttp.RunQuery(query2);
  EXPECT_TRUE(result2.has_value());

  if (input::TransferInputToViz()) {
    EXPECT_THAT(result2.value(),
                testing::ElementsAre(
                    testing::ElementsAre("cnt", "client_id", "sink_id"),
                    testing::ElementsAre(
                        "1", base::NumberToString(kFrameSinkIdA.client_id()),
                        base::NumberToString(kFrameSinkIdA.sink_id()))));
  } else {
    EXPECT_EQ(result2.value()[1][0], "0");
  }
}

TEST_P(AndroidFrameSinkManagerTest,
       RenderInputRouterLifecycleNonLayerFrameSink) {
  EXPECT_EQ(InputManagerExists(), ExpectedInputManagerCreation());

  base::test::TestTraceProcessor ttp;
  ttp.StartTrace("viz");

  // Register a non layer tree frame sink.
  MockCompositorFrameSinkClient compositor_frame_sink_client;
  mojo::Remote<mojom::CompositorFrameSink> compositor_frame_sink;
  manager_.RegisterFrameSinkId(kFrameSinkIdB, true /* report_activation */);
  manager_.CreateCompositorFrameSink(
      kFrameSinkIdB, /*bundle_id=*/std::nullopt,
      compositor_frame_sink.BindNewPipeAndPassReceiver(),
      compositor_frame_sink_client.BindInterfaceRemote(),
      /* render_input_router_config= */ nullptr);
  EXPECT_TRUE(CompositorFrameSinkExists(kFrameSinkIdB));

  if (InputManagerExists()) {
    // RIR should not be created for non layer tree frame sink.
    EXPECT_FALSE(GetMockInputManager()->RIRExistsForFrameSinkId(kFrameSinkIdB));
  }
  // Invalidating should destroy the CompositorFrameSinkImpl.
  manager_.InvalidateFrameSinkId(kFrameSinkIdB);

  EXPECT_FALSE(CompositorFrameSinkExists(kFrameSinkIdB));

  if (InputManagerExists()) {
    EXPECT_FALSE(GetMockInputManager()->RIRExistsForFrameSinkId(kFrameSinkIdB));
  }

  absl::Status status = ttp.StopAndParseTrace();
  EXPECT_TRUE(status.ok()) << status.message();

  std::string query = R"(
    SELECT COUNT(*) AS cnt,
    EXTRACT_ARG(arg_set_id, 'debug.config_is_null') as config_is_null,
    EXTRACT_ARG(arg_set_id, 'debug.frame_sink_id.frame_sink_client_id')
      as client_id,
    EXTRACT_ARG(arg_set_id, 'debug.frame_sink_id.frame_sink_id') as sink_id
    FROM slice
    WHERE slice.name = 'InputManager::OnCreateCompositorFrameSink'
  )";

  auto result = ttp.RunQuery(query);
  EXPECT_TRUE(result.has_value());

  // `result.value()` would look something like this: {{"cnt", "config_is_null",
  // "client_id", "sink_id"}, {"<num>", "<boolean>" "<clientId>", "<sinkId>"}}.
  EXPECT_EQ(result.value().size(), 2u);
  EXPECT_EQ(result.value()[1].size(), 4u);
  if (input::TransferInputToViz()) {
    EXPECT_THAT(
        result.value(),
        testing::ElementsAre(
            testing::ElementsAre("cnt", "config_is_null", "client_id",
                                 "sink_id"),
            testing::ElementsAre(
                "1", "1", base::NumberToString(kFrameSinkIdB.client_id()),
                base::NumberToString(kFrameSinkIdB.sink_id()))));
  } else {
    EXPECT_EQ(result.value()[1][0], "0");
  }
}

INSTANTIATE_TEST_SUITE_P(All,
                         AndroidFrameSinkManagerTest,
                         ::testing::Bool(),
                         [](auto& info) {
                           return info.param ? "InputOnViz_Enabled"
                                             : "InputOnViz_Disabled";
                         });
#endif  // BUILDFLAG(IS_ANDROID)

namespace {

enum RegisterOrder {};
enum UnregisterOrder {};
enum BFSOrder {};

static const RegisterOrder kRegisterOrderList[] =;
static const UnregisterOrder kUnregisterOrderList[] =;
static const BFSOrder kBFSOrderList[] =;

}  // namespace

// In practice, registering and unregistering both parent/child relationships
// and CompositorFrameSinkSupports can happen in any ordering with respect to
// each other.  These following tests verify that all the data structures
// are properly set up and cleaned up under the four permutations of orderings
// of this nesting.
class FrameSinkManagerOrderingTest : public FrameSinkManagerTest {};

class FrameSinkManagerOrderingParamTest
    : public FrameSinkManagerOrderingTest,
      public ::testing::WithParamInterface<
          std::tuple<RegisterOrder, UnregisterOrder, BFSOrder>> {};

TEST_P(FrameSinkManagerOrderingParamTest, Ordering) {}

INSTANTIATE_TEST_SUITE_P();

}  // namespace viz