chromium/chromecast/media/base/video_plane_controller_test.cc

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

#include "chromecast/media/base/video_plane_controller.h"

#include "base/task/single_thread_task_runner.h"
#include "base/test/task_environment.h"
#include "chromecast/public/cast_media_shlib.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/overlay_transform.h"

namespace chromecast {
namespace media {

namespace {

using ::testing::AllOf;
using ::testing::Field;
using ::testing::FloatEq;
using ::testing::Matcher;
using ::testing::NotNull;

// Returns a matcher that matches a chromecast::RectF's x, y, width, and height
// values.
auto RectFEquals(float x, float y, float width, float height) {
  return AllOf(Field(&chromecast::RectF::x, FloatEq(x)),
               Field(&chromecast::RectF::y, FloatEq(y)),
               Field(&chromecast::RectF::width, FloatEq(width)),
               Field(&chromecast::RectF::height, FloatEq(height)));
}

class MockVideoPlane : public VideoPlane {
 public:
  MockVideoPlane() : VideoPlane() {}
  ~MockVideoPlane() override = default;

  MOCK_METHOD(void, SetGeometry, (const RectF&, Transform), (override));
};

MockVideoPlane* g_video_plane = nullptr;

VideoPlane::Coordinates g_video_plane_coordinates =
    VideoPlane::Coordinates::kScreen;

}  // namespace

// Static function definitions for CastMediaShlib and VideoPlane.

void CastMediaShlib::Initialize(const std::vector<std::string>& argv) {
  CHECK(g_video_plane == nullptr);
  g_video_plane = new MockVideoPlane;
}

VideoPlane* CastMediaShlib::GetVideoPlane() {
  return g_video_plane;
}

void CastMediaShlib::Finalize() {
  if (g_video_plane) {
    delete g_video_plane;
    g_video_plane = nullptr;
  }
}

// Tests can set g_video_plane_coordinates to influence how VideoPlaneController
// should report coordinates (screen resolution vs graphics resolution).
VideoPlane::Coordinates VideoPlane::GetCoordinates() {
  return g_video_plane_coordinates;
}

// End of static function definitions.

namespace {

const chromecast::Size kDefaultSize(1920, 1080);

// A test fixture is used to ensure that CastMediaShlib is
// initialized/finalized, and to control the lifetime of the
// SingleThreadTaskEnvironment.
class VideoPlaneControllerTest : public ::testing::Test {
 protected:
  VideoPlaneControllerTest() { CastMediaShlib::Initialize({}); }

  ~VideoPlaneControllerTest() override { CastMediaShlib::Finalize(); }

  base::test::SingleThreadTaskEnvironment environment_;
};

TEST_F(VideoPlaneControllerTest, SetsGeometryOnVideoPlane) {
  VideoPlaneController controller(
      kDefaultSize, base::SingleThreadTaskRunner::GetCurrentDefault());
  controller.SetScreenResolution(kDefaultSize);

  const gfx::RectF video_geometry(1280, 720);

  ASSERT_THAT(g_video_plane, NotNull());
  EXPECT_CALL(*g_video_plane,
              SetGeometry(RectFEquals(0, 0, 1280, 720),
                          VideoPlane::Transform::TRANSFORM_NONE))
      .Times(1);

  controller.SetGeometry(video_geometry,
                         gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE);

  base::RunLoop().RunUntilIdle();
}

TEST_F(VideoPlaneControllerTest,
       SetsGeometryOnVideoPlaneAndTranslatesToScreenCoordinates) {
  g_video_plane_coordinates = VideoPlane::Coordinates::kScreen;

  const chromecast::Size kScreenResolution(3840, 2160);
  const chromecast::Size kGraphicsResolution(1920, 1080);

  VideoPlaneController controller(
      kGraphicsResolution, base::SingleThreadTaskRunner::GetCurrentDefault());
  controller.SetScreenResolution(kScreenResolution);

  const gfx::RectF video_geometry(960, 0, 960, 1080);

  ASSERT_THAT(g_video_plane, NotNull());
  // Note that the expected rect is scaled to the physical screen resolution.
  EXPECT_CALL(*g_video_plane,
              SetGeometry(RectFEquals(1920, 0, 1920, 2160),
                          VideoPlane::Transform::TRANSFORM_NONE))
      .Times(1);

  controller.SetGeometry(video_geometry,
                         gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE);

  base::RunLoop().RunUntilIdle();
}

TEST_F(VideoPlaneControllerTest,
       SetsGeometryOnVideoPlaneAndDoesNotScaleForGraphicsCoordinates) {
  g_video_plane_coordinates = VideoPlane::Coordinates::kGraphics;

  const chromecast::Size kScreenResolution(3840, 2160);
  const chromecast::Size kGraphicsResolution(1920, 1080);

  VideoPlaneController controller(
      kGraphicsResolution, base::SingleThreadTaskRunner::GetCurrentDefault());
  controller.SetScreenResolution(kScreenResolution);

  const gfx::RectF video_geometry(960, 0, 960, 1080);

  ASSERT_THAT(g_video_plane, NotNull());
  // Note that the expected rect is NOT scaled to the physical screen
  // resolution, due to the value of g_video_plane_coordinates.
  EXPECT_CALL(*g_video_plane,
              SetGeometry(RectFEquals(960, 0, 960, 1080),
                          VideoPlane::Transform::TRANSFORM_NONE))
      .Times(1);

  controller.SetGeometry(video_geometry,
                         gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE);

  base::RunLoop().RunUntilIdle();
}

}  // namespace
}  // namespace media
}  // namespace chromecast