910e62b5创建于 1月15日历史提交
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/protocol/coordinate_converter.h"

#include <optional>

#include "base/containers/span.h"
#include "remoting/proto/control.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"

namespace remoting::protocol {

namespace {

MATCHER_P2(EqualsOptionalDesktopVector, x, y, "") {
  if (!arg.has_value()) {
    *result_listener << "which is nullopt";
    return false;
  }
  const webrtc::DesktopVector& vec = arg.value();
  if (vec.x() == x && vec.y() == y) {
    return true;
  }
  *result_listener << "which is (" << vec.x() << ", " << vec.y() << ")";
  return false;
}

struct Screen {
  int id;
  int x;
  int y;
  int width;
  int height;
};

// o--------------+
// | screen_id 1  |
// | 200x100      |
// +--------------+------------------+
//                | screen_id 2      |
//                | 300x200          |
//                |                  |
//                +------------------+
constexpr Screen kSimpleLayout[] = {{1, 0, 0, 200, 100},
                                    {2, 200, 100, 300, 200}};

VideoLayout BuildLayout(base::span<const Screen> screens) {
  VideoLayout layout;
  for (const Screen& screen : screens) {
    VideoTrackLayout* track = layout.add_video_track();
    track->set_screen_id(screen.id);
    track->set_position_x(screen.x);
    track->set_position_y(screen.y);
    track->set_width(screen.width);
    track->set_height(screen.height);
  }
  return layout;
}

FractionalCoordinate BuildFractionalCoordinates(std::optional<int> id,
                                                float x,
                                                float y) {
  FractionalCoordinate result;
  if (id.has_value()) {
    result.set_screen_id(id.value());
  }
  result.set_x(x);
  result.set_y(y);
  return result;
}

}  // namespace

class CoordinateConverterTest : public testing::Test {
 protected:
  CoordinateConverter converter_;
};

TEST_F(CoordinateConverterTest, BasicCalculation) {
  converter_.set_video_layout(BuildLayout(kSimpleLayout));

  EXPECT_THAT(converter_.ToGlobalAbsoluteCoordinate(
                  BuildFractionalCoordinates(1, 0.0, 0.0)),
              EqualsOptionalDesktopVector(0, 0));
  EXPECT_THAT(converter_.ToGlobalAbsoluteCoordinate(
                  BuildFractionalCoordinates(1, 0.5, 0.5)),
              EqualsOptionalDesktopVector(100, 50));
  EXPECT_THAT(converter_.ToGlobalAbsoluteCoordinate(
                  BuildFractionalCoordinates(1, 1.0, 1.0)),
              EqualsOptionalDesktopVector(199, 99));
  EXPECT_THAT(converter_.ToGlobalAbsoluteCoordinate(
                  BuildFractionalCoordinates(2, 0.0, 0.0)),
              EqualsOptionalDesktopVector(200, 100));
  EXPECT_THAT(converter_.ToGlobalAbsoluteCoordinate(
                  BuildFractionalCoordinates(2, 0.5, 0.5)),
              EqualsOptionalDesktopVector(350, 200));
  EXPECT_THAT(converter_.ToGlobalAbsoluteCoordinate(
                  BuildFractionalCoordinates(2, 1.0, 1.0)),
              EqualsOptionalDesktopVector(499, 299));
}

TEST_F(CoordinateConverterTest, InvalidScreenId) {
  converter_.set_video_layout(BuildLayout(kSimpleLayout));

  EXPECT_EQ(converter_.ToGlobalAbsoluteCoordinate(
                BuildFractionalCoordinates(9, 0.5, 0.5)),
            std::nullopt);
}

TEST_F(CoordinateConverterTest, FallbackUsedIfNoScreenId) {
  converter_.set_video_layout(BuildLayout(kSimpleLayout));
  converter_.set_fallback_geometry(
      webrtc::DesktopRect::MakeXYWH(200, 100, 300, 200));

  EXPECT_THAT(converter_.ToGlobalAbsoluteCoordinate(
                  BuildFractionalCoordinates(std::nullopt, 0.5, 0.5)),
              EqualsOptionalDesktopVector(350, 200));
}

TEST_F(CoordinateConverterTest, NoScreenIdAndNoFallback) {
  converter_.set_video_layout(BuildLayout(kSimpleLayout));
  converter_.set_fallback_geometry({});

  EXPECT_EQ(converter_.ToGlobalAbsoluteCoordinate(
                BuildFractionalCoordinates(std::nullopt, 0.5, 0.5)),
            std::nullopt);
}

TEST_F(CoordinateConverterTest, Clamping) {
  converter_.set_video_layout(BuildLayout(kSimpleLayout));

  // Values outside [0, 1] should be clamped.
  EXPECT_THAT(converter_.ToGlobalAbsoluteCoordinate(
                  BuildFractionalCoordinates(1, -0.1, -0.1)),
              EqualsOptionalDesktopVector(0, 0));
  EXPECT_THAT(converter_.ToGlobalAbsoluteCoordinate(
                  BuildFractionalCoordinates(1, 1.1, 1.1)),
              EqualsOptionalDesktopVector(199, 99));
}

}  // namespace remoting::protocol