blob: 30fc4a3c1cfc520c5bf43926acdc46bd02eae45f [file] [log] [blame]
// 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