blob: 6b6219f356d32f9cd52a732a20b489e51e9015a6 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/renderer/media/media_stream_constraints_util_sets.h"
#include <cmath>
#include <string>
#include <vector>
#include "content/renderer/media/mock_constraint_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
using Point = ResolutionSet::Point;
namespace {
const int kDefaultWidth = 640;
const int kDefaultHeight = 480;
constexpr double kDefaultAspectRatio =
static_cast<double>(kDefaultWidth) / static_cast<double>(kDefaultHeight);
// Defined as macro in order to get more informative line-number information
// when a test fails.
#define EXPECT_POINT_EQ(p1, p2) \
do { \
EXPECT_DOUBLE_EQ((p1).height(), (p2).height()); \
EXPECT_DOUBLE_EQ((p1).width(), (p2).width()); \
} while (0)
// Checks if |point| is an element of |vertices| using
// Point::IsApproximatelyEqualTo() to test for equality.
void VerticesContain(const std::vector<Point>& vertices, const Point& point) {
bool result = false;
for (const auto& vertex : vertices) {
if (point.IsApproximatelyEqualTo(vertex)) {
result = true;
break;
}
}
EXPECT_TRUE(result);
}
bool AreCounterclockwise(const std::vector<Point>& vertices) {
// Single point or segment are trivial cases.
if (vertices.size() <= 2)
return true;
else if (vertices.size() > 6) // Polygons of more than 6 sides are not valid.
return false;
// The polygon defined by a resolution set is always convex and has at most 6
// sides. When producing a list of the vertices for such a polygon, it is
// important that they are returned in counterclockwise (or clockwise) order,
// to make sure that any consecutive pair of vertices (modulo the number of
// vertices) corresponds to a polygon side. Our implementation uses
// counterclockwise order.
// Compute orientation using the determinant of each diagonal in the
// polygon, using the first vertex as reference.
Point prev_diagonal = vertices[1] - vertices[0];
for (auto vertex = vertices.begin() + 2; vertex != vertices.end(); ++vertex) {
Point current_diagonal = *vertex - vertices[0];
// The determinant of the two diagonals returns the signed area of the
// parallelogram they generate. The area is positive if the diagonals are in
// counterclockwise order, zero if the diagonals have the same direction and
// negative if the diagonals are in clockwise order.
// See https://en.wikipedia.org/wiki/Determinant#2_.C3.97_2_matrices.
double det = prev_diagonal.height() * current_diagonal.width() -
current_diagonal.height() * prev_diagonal.width();
if (det <= 0)
return false;
prev_diagonal = current_diagonal;
}
return true;
}
// Determines if |vertices| is valid according to the contract for
// ResolutionCandidateSet::ComputeVertices().
bool AreValidVertices(const ResolutionSet& set,
const std::vector<Point>& vertices) {
// Verify that every vertex is included in |set|.
for (const auto& vertex : vertices) {
if (!set.ContainsPoint(vertex))
return false;
}
return AreCounterclockwise(vertices);
}
// This function provides an alternative method for computing the projection
// of |point| on the line of segment |s1||s2| to be used to compare the results
// provided by Point::ClosestPointInSegment(). Since it relies on library
// functions, it has larger error in practice than
// Point::ClosestPointInSegment(), so results must be compared with
// Point::IsApproximatelyEqualTo().
// This function only computes projections. The result may be outside the
// segment |s1||s2|.
Point ProjectionOnSegmentLine(const Point& point,
const Point& s1,
const Point& s2) {
double segment_slope =
(s2.width() - s1.width()) / (s2.height() - s1.height());
double segment_angle = std::atan(segment_slope);
double norm = std::sqrt(Point::Dot(point, point));
double angle =
(point.height() == 0 && point.width() == 0)
? 0.0
: std::atan(point.width() / point.height()) - segment_angle;
double projection_length = norm * std::cos(angle);
double projection_height = projection_length * std::cos(segment_angle);
double projection_width = projection_length * std::sin(segment_angle);
return Point(projection_height, projection_width);
}
} // namespace
class MediaStreamConstraintsUtilSetsTest : public testing::Test {
protected:
using P = Point;
Point SelectClosestPointToIdeal(const ResolutionSet& set) {
return set.SelectClosestPointToIdeal(
factory_.CreateWebMediaConstraints().Basic(), kDefaultHeight,
kDefaultWidth);
}
MockConstraintFactory factory_;
};
// This test tests the test-harness function AreValidVertices.
TEST_F(MediaStreamConstraintsUtilSetsTest, VertexListValidity) {
EXPECT_TRUE(AreCounterclockwise({P(1, 1)}));
EXPECT_TRUE(AreCounterclockwise({P(1, 1)}));
EXPECT_TRUE(AreCounterclockwise({P(1, 0), P(0, 1)}));
EXPECT_TRUE(AreCounterclockwise({P(1, 1), P(0, 0), P(1, 0)}));
// Not in counterclockwise order.
EXPECT_FALSE(AreCounterclockwise({P(1, 0), P(0, 0), P(1, 1)}));
// Final vertex aligned with the previous two vertices.
EXPECT_FALSE(AreCounterclockwise({P(1, 0), P(1, 1), P(1, 1.5), P(1, 0.1)}));
// Not in counterclockwise order.
EXPECT_FALSE(
AreCounterclockwise({P(1, 0), P(3, 0), P(2, 2), P(3.1, 1), P(0, 1)}));
EXPECT_TRUE(AreCounterclockwise(
{P(1, 0), P(3, 0), P(3.1, 1), P(3, 2), P(1, 2), P(0.9, 1)}));
// Not in counterclockwise order.
EXPECT_FALSE(AreCounterclockwise(
{P(1, 0), P(3, 0), P(3.1, 1), P(1, 2), P(3, 2), P(0.9, 1)}));
// Counterclockwise, but more than 6 vertices.
EXPECT_FALSE(AreCounterclockwise(
{P(1, 0), P(3, 0), P(3.1, 1), P(3, 2), P(2, 2.1), P(1, 2), P(0.9, 1)}));
}
TEST_F(MediaStreamConstraintsUtilSetsTest, PointOperations) {
const Point kZero(0, 0);
// Basic equality and inequality
EXPECT_EQ(P(0, 0), kZero);
EXPECT_EQ(P(50, 50), P(50, 50));
EXPECT_NE(kZero, P(50, 50));
EXPECT_NE(P(50, 50), P(100, 100));
EXPECT_NE(P(50, 50), P(100, 50));
// Operations with zero.
EXPECT_EQ(kZero, kZero + kZero);
EXPECT_EQ(kZero, kZero - kZero);
EXPECT_EQ(kZero, 0.0 * kZero);
EXPECT_EQ(0.0, P::Dot(kZero, kZero));
EXPECT_EQ(0.0, P::SquareEuclideanDistance(kZero, kZero));
EXPECT_EQ(kZero, P::ClosestPointInSegment(kZero, kZero, kZero));
// Operations with zero and nonzero values.
EXPECT_EQ(P(50, 50), kZero + P(50, 50));
EXPECT_EQ(P(50, 50) + kZero, kZero + P(50, 50));
EXPECT_EQ(P(50, 50), P(50, 50) - kZero);
EXPECT_EQ(kZero, P(50, 50) - P(50, 50));
EXPECT_EQ(kZero, 0.0 * P(50, 50));
EXPECT_EQ(0.0, P::Dot(kZero, P(50, 50)));
EXPECT_EQ(0.0, P::Dot(P(50, 50), kZero));
EXPECT_EQ(5000, P::SquareEuclideanDistance(kZero, P(50, 50)));
EXPECT_EQ(P::SquareEuclideanDistance(P(50, 50), kZero),
P::SquareEuclideanDistance(kZero, P(50, 50)));
EXPECT_EQ(kZero, P::ClosestPointInSegment(kZero, kZero, P(50, 50)));
EXPECT_EQ(kZero, P::ClosestPointInSegment(kZero, P(50, 50), kZero));
EXPECT_EQ(P(50, 50),
P::ClosestPointInSegment(P(50, 50), P(50, 50), P(50, 50)));
// Operations with nonzero values.
// Additions.
EXPECT_EQ(P(100, 50), P(50, 50) + P(50, 0));
EXPECT_EQ(P(100, 50), P(50, 0) + P(50, 50));
// Substractions.
EXPECT_EQ(P(50, 50), P(100, 100) - P(50, 50));
EXPECT_EQ(P(50, 50), P(100, 50) - P(50, 0));
EXPECT_EQ(P(50, 0), P(100, 50) - P(50, 50));
// Scalar-vector products.
EXPECT_EQ(P(50, 50), 1.0 * P(50, 50));
EXPECT_EQ(P(75, 75), 1.5 * P(50, 50));
EXPECT_EQ(P(200, 100), 2.0 * P(100, 50));
EXPECT_EQ(2.0 * (P(100, 100) + P(100, 50)),
2.0 * P(100, 100) + 2.0 * P(100, 50));
// Dot products.
EXPECT_EQ(2 * 50 * 100, P::Dot(P(50, 50), P(100, 100)));
EXPECT_EQ(P::Dot(P(100, 100), P(50, 50)), P::Dot(P(50, 50), P(100, 100)));
EXPECT_EQ(0, P::Dot(P(100, 0), P(0, 100)));
// Distances.
EXPECT_EQ(25, P::SquareEuclideanDistance(P(4, 0), P(0, 3)));
EXPECT_EQ(75 * 75, P::SquareEuclideanDistance(P(100, 0), P(25, 0)));
EXPECT_EQ(75 * 75, P::SquareEuclideanDistance(P(0, 100), P(0, 25)));
EXPECT_EQ(5 * 5 + 9 * 9, P::SquareEuclideanDistance(P(5, 1), P(10, 10)));
// Closest point to segment from (10,0) to (50,0).
EXPECT_EQ(P(25, 0), P::ClosestPointInSegment(P(25, 25), P(10, 0), P(50, 0)));
EXPECT_EQ(P(50, 0),
P::ClosestPointInSegment(P(100, 100), P(10, 0), P(50, 0)));
EXPECT_EQ(P(10, 0), P::ClosestPointInSegment(P(0, 100), P(10, 0), P(50, 0)));
// Closest point to segment from (0,10) to (0,50).
EXPECT_EQ(P(0, 25), P::ClosestPointInSegment(P(25, 25), P(0, 10), P(0, 50)));
EXPECT_EQ(P(0, 50),
P::ClosestPointInSegment(P(100, 100), P(0, 10), P(0, 50)));
EXPECT_EQ(P(0, 10), P::ClosestPointInSegment(P(100, 0), P(0, 10), P(0, 50)));
// Closest point to segment from (0,10) to (10,0).
EXPECT_EQ(P(5, 5), P::ClosestPointInSegment(P(25, 25), P(0, 10), P(10, 0)));
EXPECT_EQ(P(5, 5), P::ClosestPointInSegment(P(100, 100), P(0, 10), P(10, 0)));
EXPECT_EQ(P(10, 0), P::ClosestPointInSegment(P(100, 0), P(0, 10), P(10, 0)));
EXPECT_EQ(P(0, 10), P::ClosestPointInSegment(P(0, 100), P(0, 10), P(10, 0)));
}
TEST_F(MediaStreamConstraintsUtilSetsTest, ResolutionUnconstrained) {
ResolutionSet set;
EXPECT_TRUE(set.ContainsPoint(0, 0));
EXPECT_TRUE(set.ContainsPoint(1, 1));
EXPECT_TRUE(set.ContainsPoint(2000, 2000));
EXPECT_FALSE(set.IsHeightEmpty());
EXPECT_FALSE(set.IsWidthEmpty());
EXPECT_FALSE(set.IsAspectRatioEmpty());
}
TEST_F(MediaStreamConstraintsUtilSetsTest, ResolutionConstrained) {
ResolutionSet set = ResolutionSet::FromHeight(10, 100);
EXPECT_FALSE(set.ContainsPoint(0, 0));
EXPECT_TRUE(set.ContainsPoint(10, 10));
EXPECT_TRUE(set.ContainsPoint(50, 50));
EXPECT_TRUE(set.ContainsPoint(100, 100));
EXPECT_FALSE(set.ContainsPoint(500, 500));
EXPECT_FALSE(set.IsHeightEmpty());
EXPECT_FALSE(set.IsWidthEmpty());
EXPECT_FALSE(set.IsAspectRatioEmpty());
set = ResolutionSet::FromHeight(0, 100);
EXPECT_TRUE(set.ContainsPoint(0, 0));
EXPECT_TRUE(set.ContainsPoint(10, 10));
EXPECT_TRUE(set.ContainsPoint(50, 50));
EXPECT_TRUE(set.ContainsPoint(100, 100));
EXPECT_FALSE(set.ContainsPoint(500, 500));
EXPECT_FALSE(set.IsHeightEmpty());
EXPECT_FALSE(set.IsWidthEmpty());
EXPECT_FALSE(set.IsAspectRatioEmpty());
set = ResolutionSet::FromHeight(100, ResolutionSet::kMaxDimension);
EXPECT_FALSE(set.ContainsPoint(0, 0));
EXPECT_FALSE(set.ContainsPoint(10, 10));
EXPECT_FALSE(set.ContainsPoint(50, 50));
EXPECT_TRUE(set.ContainsPoint(100, 100));
EXPECT_TRUE(set.ContainsPoint(500, 500));
EXPECT_FALSE(set.IsHeightEmpty());
EXPECT_FALSE(set.IsWidthEmpty());
EXPECT_FALSE(set.IsAspectRatioEmpty());
set = ResolutionSet::FromWidth(10, 100);
EXPECT_FALSE(set.ContainsPoint(0, 0));
EXPECT_TRUE(set.ContainsPoint(10, 10));
EXPECT_TRUE(set.ContainsPoint(50, 50));
EXPECT_TRUE(set.ContainsPoint(100, 100));
EXPECT_FALSE(set.ContainsPoint(500, 500));
EXPECT_FALSE(set.IsHeightEmpty());
EXPECT_FALSE(set.IsWidthEmpty());
EXPECT_FALSE(set.IsAspectRatioEmpty());
set = ResolutionSet::FromWidth(0, 100);
EXPECT_TRUE(set.ContainsPoint(0, 0));
EXPECT_TRUE(set.ContainsPoint(10, 10));
EXPECT_TRUE(set.ContainsPoint(50, 50));
EXPECT_TRUE(set.ContainsPoint(100, 100));
EXPECT_FALSE(set.ContainsPoint(500, 500));
EXPECT_FALSE(set.IsHeightEmpty());
EXPECT_FALSE(set.IsWidthEmpty());
EXPECT_FALSE(set.IsAspectRatioEmpty());
set = ResolutionSet::FromWidth(100, ResolutionSet::kMaxDimension);
EXPECT_FALSE(set.ContainsPoint(0, 0));
EXPECT_FALSE(set.ContainsPoint(10, 10));
EXPECT_FALSE(set.ContainsPoint(50, 50));
EXPECT_TRUE(set.ContainsPoint(100, 100));
EXPECT_TRUE(set.ContainsPoint(500, 500));
EXPECT_FALSE(set.IsHeightEmpty());
EXPECT_FALSE(set.IsWidthEmpty());
EXPECT_FALSE(set.IsAspectRatioEmpty());
set = ResolutionSet::FromAspectRatio(1.0, 2.0);
EXPECT_TRUE(set.ContainsPoint(0, 0));
EXPECT_TRUE(set.ContainsPoint(10, 10));
EXPECT_TRUE(set.ContainsPoint(10, 20));
EXPECT_TRUE(set.ContainsPoint(100, 100));
EXPECT_TRUE(set.ContainsPoint(2000, 4000));
EXPECT_FALSE(set.ContainsPoint(1, 50));
EXPECT_FALSE(set.ContainsPoint(50, 1));
EXPECT_FALSE(set.IsHeightEmpty());
EXPECT_FALSE(set.IsWidthEmpty());
EXPECT_FALSE(set.IsAspectRatioEmpty());
set = ResolutionSet::FromAspectRatio(0.0, 2.0);
EXPECT_TRUE(set.ContainsPoint(0, 0));
EXPECT_TRUE(set.ContainsPoint(10, 10));
EXPECT_TRUE(set.ContainsPoint(10, 20));
EXPECT_TRUE(set.ContainsPoint(100, 100));
EXPECT_TRUE(set.ContainsPoint(2000, 4000));
EXPECT_FALSE(set.ContainsPoint(1, 50));
EXPECT_TRUE(set.ContainsPoint(50, 1));
EXPECT_FALSE(set.IsHeightEmpty());
EXPECT_FALSE(set.IsWidthEmpty());
EXPECT_FALSE(set.IsAspectRatioEmpty());
set = ResolutionSet::FromAspectRatio(1.0, HUGE_VAL);
EXPECT_TRUE(set.ContainsPoint(0, 0));
EXPECT_TRUE(set.ContainsPoint(10, 10));
EXPECT_TRUE(set.ContainsPoint(10, 20));
EXPECT_TRUE(set.ContainsPoint(100, 100));
EXPECT_TRUE(set.ContainsPoint(2000, 4000));
EXPECT_TRUE(set.ContainsPoint(1, 50));
EXPECT_FALSE(set.ContainsPoint(50, 1));
EXPECT_FALSE(set.IsHeightEmpty());
EXPECT_FALSE(set.IsWidthEmpty());
EXPECT_FALSE(set.IsAspectRatioEmpty());
}
TEST_F(MediaStreamConstraintsUtilSetsTest, ResolutionTrivialEmptiness) {
ResolutionSet set = ResolutionSet::FromHeight(100, 10);
EXPECT_TRUE(set.IsEmpty());
EXPECT_TRUE(set.IsHeightEmpty());
EXPECT_FALSE(set.IsWidthEmpty());
EXPECT_FALSE(set.IsAspectRatioEmpty());
set = ResolutionSet::FromWidth(100, 10);
EXPECT_TRUE(set.IsEmpty());
EXPECT_FALSE(set.IsHeightEmpty());
EXPECT_TRUE(set.IsWidthEmpty());
EXPECT_FALSE(set.IsAspectRatioEmpty());
set = ResolutionSet::FromAspectRatio(100.0, 10.0);
EXPECT_TRUE(set.IsEmpty());
EXPECT_FALSE(set.IsHeightEmpty());
EXPECT_FALSE(set.IsWidthEmpty());
EXPECT_TRUE(set.IsAspectRatioEmpty());
}
TEST_F(MediaStreamConstraintsUtilSetsTest, ResolutionLineConstraintsEmptiness) {
ResolutionSet set(1, 1, 1, 1, 1, 1);
EXPECT_FALSE(set.IsEmpty());
EXPECT_FALSE(set.ContainsPoint(0, 0));
EXPECT_TRUE(set.ContainsPoint(1, 1));
EXPECT_FALSE(set.ContainsPoint(1, 0));
EXPECT_FALSE(set.ContainsPoint(0, 1));
// Three lines that do not intersect in the same point is empty.
set = ResolutionSet(1, 1, 1, 1, 0.5, 0.5);
EXPECT_TRUE(set.IsEmpty());
EXPECT_TRUE(set.IsAspectRatioEmpty());
EXPECT_FALSE(set.ContainsPoint(0, 0));
EXPECT_FALSE(set.ContainsPoint(1, 1));
EXPECT_FALSE(set.ContainsPoint(1, 0));
EXPECT_FALSE(set.ContainsPoint(0, 1));
}
TEST_F(MediaStreamConstraintsUtilSetsTest, ResolutionBoxEmptiness) {
const int kMin = 100;
const int kMax = 200;
// Max aspect ratio below box.
ResolutionSet set(kMin, kMax, kMin, kMax, 0.4, 0.4);
EXPECT_TRUE(set.IsEmpty());
EXPECT_TRUE(set.IsAspectRatioEmpty());
// Min aspect ratio above box.
set = ResolutionSet(kMin, kMax, kMin, kMax, 3.0, HUGE_VAL);
EXPECT_TRUE(set.IsEmpty());
EXPECT_TRUE(set.IsAspectRatioEmpty());
// Min aspect ratio crosses box.
set = ResolutionSet(kMin, kMax, kMin, kMax, 1.0, HUGE_VAL);
EXPECT_FALSE(set.IsEmpty());
// Max aspect ratio crosses box.
set = ResolutionSet(kMin, kMax, kMin, kMax, 0.0, 1.0);
EXPECT_FALSE(set.IsEmpty());
// Min and max aspect ratios cross box.
set = ResolutionSet(kMin, kMax, kMin, kMax, 0.9, 1.1);
EXPECT_FALSE(set.IsEmpty());
// Min and max aspect ratios cover box.
set = ResolutionSet(kMin, kMax, kMin, kMax, 0.2, 100);
EXPECT_FALSE(set.IsEmpty());
}
TEST_F(MediaStreamConstraintsUtilSetsTest, ResolutionPointIntersection) {
ResolutionSet set1(1, 2, 1, 2, 0.0, HUGE_VAL);
ResolutionSet set2 = ResolutionSet::FromExactAspectRatio(0.5);
auto intersection = set1.Intersection(set2);
// The intersection should contain only the point (h=2, w=1)
EXPECT_TRUE(intersection.ContainsPoint(2, 1));
// It should not contain any point in the vicinity of the included point
// (integer version).
EXPECT_FALSE(intersection.ContainsPoint(1, 0));
EXPECT_FALSE(intersection.ContainsPoint(2, 0));
EXPECT_FALSE(intersection.ContainsPoint(3, 0));
EXPECT_FALSE(intersection.ContainsPoint(1, 1));
EXPECT_FALSE(intersection.ContainsPoint(3, 1));
EXPECT_FALSE(intersection.ContainsPoint(1, 2));
EXPECT_FALSE(intersection.ContainsPoint(2, 2));
EXPECT_FALSE(intersection.ContainsPoint(3, 2));
// It should not contain any point in the vicinity of the included point
// (floating-point version).
EXPECT_FALSE(intersection.ContainsPoint(P(2.0001, 1.0001)));
EXPECT_FALSE(intersection.ContainsPoint(P(2.0001, 1.0)));
EXPECT_FALSE(intersection.ContainsPoint(P(2.0001, 0.9999)));
EXPECT_FALSE(intersection.ContainsPoint(P(2.0, 1.0001)));
EXPECT_FALSE(intersection.ContainsPoint(P(2.0, 0.9999)));
EXPECT_FALSE(intersection.ContainsPoint(P(1.9999, 1.0001)));
EXPECT_FALSE(intersection.ContainsPoint(P(1.9999, 1.0)));
EXPECT_FALSE(intersection.ContainsPoint(P(1.9999, 0.9999)));
}
TEST_F(MediaStreamConstraintsUtilSetsTest, ResolutionLineIntersection) {
ResolutionSet set1(1, 2, 1, 2, 0.0, HUGE_VAL);
ResolutionSet set2 = ResolutionSet::FromExactAspectRatio(1.0);
// The intersection should contain (1,1) and (2,2)
auto intersection = set1.Intersection(set2);
EXPECT_TRUE(intersection.ContainsPoint(1, 1));
EXPECT_TRUE(intersection.ContainsPoint(2, 2));
// It should not contain the other points in the bounding box.
EXPECT_FALSE(intersection.ContainsPoint(1, 2));
EXPECT_FALSE(intersection.ContainsPoint(2, 1));
}
TEST_F(MediaStreamConstraintsUtilSetsTest, ResolutionBoxIntersection) {
const int kMin1 = 0;
const int kMax1 = 2;
ResolutionSet set1(kMin1, kMax1, kMin1, kMax1, 0.0, HUGE_VAL);
const int kMin2 = 1;
const int kMax2 = 3;
ResolutionSet set2(kMin2, kMax2, kMin2, kMax2, 0.0, HUGE_VAL);
auto intersection = set1.Intersection(set2);
for (int i = kMin1; i <= kMax2; ++i) {
for (int j = kMin1; j <= kMax2; ++j) {
if (i >= kMin2 && j >= kMin2 && i <= kMax1 && j <= kMax1)
EXPECT_TRUE(intersection.ContainsPoint(i, j));
else
EXPECT_FALSE(intersection.ContainsPoint(i, j));
}
}
}
TEST_F(MediaStreamConstraintsUtilSetsTest, ResolutionPointSetClosestPoint) {
const int kHeight = 10;
const int kWidth = 10;
const double kAspectRatio = 1.0;
ResolutionSet set(kHeight, kHeight, kWidth, kWidth, kAspectRatio,
kAspectRatio);
for (int height = 0; height < 100; height += 10) {
for (int width = 0; width < 100; width += 10) {
EXPECT_EQ(P(kHeight, kWidth), set.ClosestPointTo(P(height, width)));
}
}
}
TEST_F(MediaStreamConstraintsUtilSetsTest, ResolutionLineSetClosestPoint) {
{
const int kHeight = 10;
auto set = ResolutionSet::FromExactHeight(kHeight);
for (int height = 0; height < 100; height += 10) {
for (int width = 0; width < 100; width += 10) {
EXPECT_EQ(P(kHeight, width), set.ClosestPointTo(P(height, width)));
}
}
const int kWidth = 10;
set = ResolutionSet::FromExactWidth(kWidth);
for (int height = 0; height < 100; height += 10) {
for (int width = 0; width < 100; width += 10) {
EXPECT_EQ(P(height, kWidth), set.ClosestPointTo(P(height, width)));
}
}
}
{
const double kAspectRatios[] = {0.0, 0.1, 0.2, 0.5,
1.0, 2.0, 5.0, HUGE_VAL};
for (double aspect_ratio : kAspectRatios) {
auto set = ResolutionSet::FromExactAspectRatio(aspect_ratio);
for (int height = 0; height < 100; height += 10) {
for (int width = 0; width < 100; width += 10) {
Point point(height, width);
Point expected =
ProjectionOnSegmentLine(point, P(0, 0), P(1, aspect_ratio));
Point actual = set.ClosestPointTo(point);
// This requires higher tolerance than ExpectPointEx due to the larger
// error of the alternative projection method.
EXPECT_TRUE(expected.IsApproximatelyEqualTo(actual));
}
}
}
}
}
TEST_F(MediaStreamConstraintsUtilSetsTest, ResolutionGeneralSetClosestPoint) {
// This set contains the following vertices:
// (10, 10), (20, 10), (100, 50), (100, 100), (100/1.5, 100), (10, 15)
ResolutionSet set(10, 100, 10, 100, 0.5, 1.5);
// Check that vertices are the closest points to themselves.
auto vertices = set.ComputeVertices();
for (auto& vertex : vertices)
EXPECT_EQ(vertex, set.ClosestPointTo(vertex));
// Point inside the set.
EXPECT_EQ(P(11, 11), set.ClosestPointTo(P(11, 11)));
// Close to horizontal segment (10, 10) (20, 10).
EXPECT_EQ(P(15, 10), set.ClosestPointTo(P(15, 9)));
// Close to horizontal segment (100, 100) (100/1.5, 100).
EXPECT_EQ(P(99, 100), set.ClosestPointTo(P(99, 200)));
// Close to vertical segment (10, 15) (10, 10).
EXPECT_EQ(P(10, 12.5), set.ClosestPointTo(P(2, 12.5)));
// Close to vertical segment (100, 50) (100, 100).
EXPECT_EQ(P(100, 75), set.ClosestPointTo(P(120, 75)));
// Close to oblique segment (20, 10) (100, 50)
{
Point point(70, 15);
Point expected = ProjectionOnSegmentLine(point, P(20, 10), P(100, 50));
Point actual = set.ClosestPointTo(point);
EXPECT_POINT_EQ(expected, actual);
}
// Close to oblique segment (100/1.5, 100) (10, 15)
{
Point point(12, 70);
Point expected =
ProjectionOnSegmentLine(point, P(100 / 1.5, 100), P(10, 15));
Point actual = set.ClosestPointTo(point);
EXPECT_POINT_EQ(expected, actual);
}
// Points close to vertices.
EXPECT_EQ(P(10, 10), set.ClosestPointTo(P(9, 9)));
EXPECT_EQ(P(20, 10), set.ClosestPointTo(P(20, 9)));
EXPECT_EQ(P(100, 50), set.ClosestPointTo(P(101, 50)));
EXPECT_EQ(P(100, 100), set.ClosestPointTo(P(101, 101)));
EXPECT_EQ(P(100 / 1.5, 100), set.ClosestPointTo(P(100 / 1.5, 101)));
EXPECT_EQ(P(10, 15), set.ClosestPointTo(P(9, 15)));
}
TEST_F(MediaStreamConstraintsUtilSetsTest, ResolutionIdealIntersects) {
ResolutionSet set(100, 1000, 100, 1000, 0.5, 2.0);
const int kIdealHeight = 500;
const int kIdealWidth = 1000;
const double kIdealAspectRatio = 1.5;
// Ideal height.
{
factory_.Reset();
factory_.basic().height.SetIdeal(kIdealHeight);
Point point = SelectClosestPointToIdeal(set);
EXPECT_POINT_EQ(Point(kIdealHeight, kIdealHeight * kDefaultAspectRatio),
point);
}
// Ideal width.
{
factory_.Reset();
factory_.basic().width.SetIdeal(kIdealWidth);
Point point = SelectClosestPointToIdeal(set);
EXPECT_POINT_EQ(Point(kIdealWidth / kDefaultAspectRatio, kIdealWidth),
point);
}
// Ideal aspect ratio.
{
factory_.Reset();
factory_.basic().aspect_ratio.SetIdeal(kIdealAspectRatio);
Point point = SelectClosestPointToIdeal(set);
EXPECT_DOUBLE_EQ(kDefaultHeight, point.height());
EXPECT_DOUBLE_EQ(kDefaultHeight * kIdealAspectRatio, point.width());
}
// Ideal height and width.
{
factory_.Reset();
factory_.basic().height.SetIdeal(kIdealHeight);
factory_.basic().width.SetIdeal(kIdealWidth);
Point point = SelectClosestPointToIdeal(set);
EXPECT_POINT_EQ(Point(kIdealHeight, kIdealWidth), point);
}
// Ideal height and aspect-ratio.
{
factory_.Reset();
factory_.basic().height.SetIdeal(kIdealHeight);
factory_.basic().aspect_ratio.SetIdeal(kIdealAspectRatio);
Point point = SelectClosestPointToIdeal(set);
EXPECT_POINT_EQ(Point(kIdealHeight, kIdealHeight * kIdealAspectRatio),
point);
}
// Ideal width and aspect-ratio.
{
factory_.Reset();
factory_.basic().width.SetIdeal(kIdealWidth);
factory_.basic().aspect_ratio.SetIdeal(kIdealAspectRatio);
Point point = SelectClosestPointToIdeal(set);
EXPECT_POINT_EQ(Point(kIdealWidth / kIdealAspectRatio, kIdealWidth), point);
}
// Ideal height, width and aspect-ratio.
{
factory_.Reset();
factory_.basic().height.SetIdeal(kIdealHeight);
factory_.basic().width.SetIdeal(kIdealWidth);
factory_.basic().aspect_ratio.SetIdeal(kIdealAspectRatio);
Point point = SelectClosestPointToIdeal(set);
// Ideal aspect ratio should be ignored.
EXPECT_POINT_EQ(Point(kIdealHeight, kIdealWidth), point);
}
}
TEST_F(MediaStreamConstraintsUtilSetsTest, ResolutionIdealOutsideSinglePoint) {
// This set is a triangle with vertices (100,100), (1000,100) and (1000,1000).
ResolutionSet set(100, 1000, 100, 1000, 0.0, 1.0);
const int kIdealHeight = 50;
const int kIdealWidth = 1100;
const double kIdealAspectRatio = 0.09;
const Point kVertex1(100, 100);
const Point kVertex2(1000, 100);
const Point kVertex3(1000, 1000);
// Ideal height.
{
factory_.Reset();
factory_.basic().height.SetIdeal(kIdealHeight);
Point point = SelectClosestPointToIdeal(set);
EXPECT_POINT_EQ(kVertex1, point);
}
// Ideal width.
{
factory_.Reset();
factory_.basic().width.SetIdeal(kIdealWidth);
Point point = SelectClosestPointToIdeal(set);
EXPECT_POINT_EQ(kVertex3, point);
}
// Ideal aspect ratio.
{
factory_.Reset();
factory_.basic().aspect_ratio.SetIdeal(kIdealAspectRatio);
Point point = SelectClosestPointToIdeal(set);
EXPECT_POINT_EQ(kVertex2, point);
}
// Ideal height and width.
{
factory_.Reset();
factory_.basic().height.SetIdeal(kIdealHeight);
factory_.basic().width.SetIdeal(kIdealWidth);
Point point = SelectClosestPointToIdeal(set);
Point expected = set.ClosestPointTo(Point(kIdealHeight, kIdealWidth));
EXPECT_POINT_EQ(expected, point);
}
// Ideal height and aspect-ratio.
{
factory_.Reset();
factory_.basic().height.SetIdeal(kIdealHeight);
factory_.basic().aspect_ratio.SetIdeal(kIdealAspectRatio);
Point point = SelectClosestPointToIdeal(set);
Point expected = set.ClosestPointTo(
Point(kIdealHeight, kIdealHeight * kIdealAspectRatio));
EXPECT_POINT_EQ(expected, point);
}
// Ideal width and aspect-ratio.
{
factory_.Reset();
factory_.basic().width.SetIdeal(kIdealWidth);
factory_.basic().aspect_ratio.SetIdeal(kIdealAspectRatio);
Point point = SelectClosestPointToIdeal(set);
Point expected =
set.ClosestPointTo(Point(kIdealWidth / kIdealAspectRatio, kIdealWidth));
EXPECT_POINT_EQ(expected, point);
}
// Ideal height, width and aspect-ratio.
{
factory_.Reset();
factory_.basic().height.SetIdeal(kIdealHeight);
factory_.basic().width.SetIdeal(kIdealWidth);
factory_.basic().aspect_ratio.SetIdeal(kIdealAspectRatio);
Point point = SelectClosestPointToIdeal(set);
// kIdealAspectRatio is ignored if all three ideals are given.
Point expected = set.ClosestPointTo(Point(kIdealHeight, kIdealWidth));
EXPECT_POINT_EQ(expected, point);
}
}
TEST_F(MediaStreamConstraintsUtilSetsTest,
ResolutionIdealOutsideMultiplePoints) {
// This set is a triangle with vertices (100,100), (1000,100) and (1000,1000).
ResolutionSet set(100, 1000, 100, 1000, 0.0, 1.0);
const int kIdealHeight = 1100;
const int kIdealWidth = 50;
const double kIdealAspectRatio = 11.0;
const Point kVertex1(100, 100);
const Point kVertex2(1000, 100);
const Point kVertex3(1000, 1000);
// Ideal height.
{
factory_.Reset();
factory_.basic().height.SetIdeal(kIdealHeight);
Point point = SelectClosestPointToIdeal(set);
// Parallel to the side between kVertex2 and kVertex3. Point closest to
// default aspect ratio is kVertex3.
EXPECT_POINT_EQ(kVertex3, point);
}
// Ideal width.
{
factory_.Reset();
factory_.basic().width.SetIdeal(kIdealWidth);
Point point = SelectClosestPointToIdeal(set);
// Parallel to the side between kVertex1 and kVertex2. Point closest to
// default aspect ratio is kVertex1.
EXPECT_POINT_EQ(kVertex1, point);
}
// Ideal aspect ratio.
{
factory_.Reset();
factory_.basic().aspect_ratio.SetIdeal(kIdealAspectRatio);
Point point = SelectClosestPointToIdeal(set);
// The side between kVertex1 and kVertex3 is closest. The points closest to
// default dimensions are (kDefaultHeight, kDefaultHeight * AR)
// and (kDefaultWidth / AR, kDefaultWidth). Since the aspect ratio of the
// polygon side is less than the default, the algorithm preserves the
// default width.
Point expected(kDefaultWidth / kVertex1.AspectRatio(), kDefaultWidth);
EXPECT_POINT_EQ(expected, point);
EXPECT_TRUE(set.ContainsPoint(expected));
}
}
TEST_F(MediaStreamConstraintsUtilSetsTest,
ResolutionUnconstrainedExtremeIdeal) {
ResolutionSet set;
// Ideal height.
{
factory_.Reset();
factory_.basic().height.SetIdeal(std::numeric_limits<long>::max());
Point point = SelectClosestPointToIdeal(set);
EXPECT_POINT_EQ(
Point(ResolutionSet::kMaxDimension, ResolutionSet::kMaxDimension),
point);
factory_.basic().height.SetIdeal(0);
point = SelectClosestPointToIdeal(set);
EXPECT_POINT_EQ(Point(0, 0), point);
}
// Ideal width.
{
factory_.Reset();
factory_.basic().width.SetIdeal(std::numeric_limits<long>::max());
Point point = SelectClosestPointToIdeal(set);
EXPECT_POINT_EQ(Point(ResolutionSet::kMaxDimension / kDefaultAspectRatio,
ResolutionSet::kMaxDimension),
point);
factory_.basic().width.SetIdeal(0);
point = SelectClosestPointToIdeal(set);
EXPECT_POINT_EQ(Point(0, 0), point);
}
// Ideal Aspect Ratio.
{
factory_.Reset();
factory_.basic().aspect_ratio.SetIdeal(HUGE_VAL);
Point point = SelectClosestPointToIdeal(set);
EXPECT_POINT_EQ(Point(0, ResolutionSet::kMaxDimension), point);
factory_.basic().aspect_ratio.SetIdeal(0.0);
point = SelectClosestPointToIdeal(set);
EXPECT_POINT_EQ(Point(ResolutionSet::kMaxDimension, 0), point);
}
}
TEST_F(MediaStreamConstraintsUtilSetsTest, ResolutionVertices) {
// Empty set.
{
ResolutionSet set(1000, 100, 1000, 100, 0.5, 1.5);
ASSERT_TRUE(set.IsEmpty());
auto vertices = set.ComputeVertices();
EXPECT_EQ(0U, vertices.size());
EXPECT_TRUE(AreValidVertices(set, vertices));
}
// Three lines that intersect at the same point.
{
ResolutionSet set(1, 1, 1, 1, 1, 1);
EXPECT_FALSE(set.IsEmpty());
auto vertices = set.ComputeVertices();
EXPECT_EQ(1U, vertices.size());
VerticesContain(vertices, Point(1, 1));
EXPECT_TRUE(AreValidVertices(set, vertices));
}
// A line segment with the lower-left and upper-right corner of the box.
{
ResolutionSet set(0, 100, 0, 100, 1.0, 1.0);
EXPECT_FALSE(set.IsEmpty());
auto vertices = set.ComputeVertices();
EXPECT_EQ(2U, vertices.size());
VerticesContain(vertices, Point(0, 0));
VerticesContain(vertices, Point(100, 100));
EXPECT_TRUE(AreValidVertices(set, vertices));
set = ResolutionSet(0, 100, 0, 100, 1.0, HUGE_VAL);
EXPECT_FALSE(set.IsEmpty());
vertices = set.ComputeVertices();
EXPECT_EQ(3U, vertices.size());
VerticesContain(vertices, Point(0, 0));
VerticesContain(vertices, Point(100, 100));
VerticesContain(vertices, Point(0, 100));
EXPECT_TRUE(AreValidVertices(set, vertices));
set = ResolutionSet(0, 100, 0, 100, 0, 1.0);
EXPECT_FALSE(set.IsEmpty());
vertices = set.ComputeVertices();
EXPECT_EQ(3U, vertices.size());
VerticesContain(vertices, Point(0, 0));
VerticesContain(vertices, Point(100, 100));
VerticesContain(vertices, Point(100, 0));
EXPECT_TRUE(AreValidVertices(set, vertices));
}
// A line segment that crosses the bottom and right sides of the box.
{
const double kAspectRatio = 50.0 / 75.0;
ResolutionSet set(50, 100, 50, 100, kAspectRatio, kAspectRatio);
auto vertices = set.ComputeVertices();
EXPECT_EQ(2U, vertices.size());
VerticesContain(vertices, Point(50 / kAspectRatio, 50));
VerticesContain(vertices, Point(100, 100.0 * kAspectRatio));
EXPECT_TRUE(AreValidVertices(set, vertices));
set = ResolutionSet(50, 100, 50, 100, kAspectRatio, HUGE_VAL);
vertices = set.ComputeVertices();
EXPECT_EQ(5U, vertices.size());
VerticesContain(vertices, Point(50 / kAspectRatio, 50));
VerticesContain(vertices, Point(100, 100.0 * kAspectRatio));
VerticesContain(vertices, Point(50, 50));
VerticesContain(vertices, Point(50, 100));
VerticesContain(vertices, Point(100, 100));
EXPECT_TRUE(AreValidVertices(set, vertices));
set = ResolutionSet(50, 100, 50, 100, 0.0, kAspectRatio);
vertices = set.ComputeVertices();
EXPECT_EQ(3U, vertices.size());
VerticesContain(vertices, Point(50 / kAspectRatio, 50));
VerticesContain(vertices, Point(100, 100.0 * kAspectRatio));
VerticesContain(vertices, Point(100, 50));
EXPECT_TRUE(AreValidVertices(set, vertices));
}
// A line segment that crosses the left and top sides of the box.
{
const double kAspectRatio = 75.0 / 50.0;
ResolutionSet set(50, 100, 50, 100, kAspectRatio, kAspectRatio);
auto vertices = set.ComputeVertices();
EXPECT_EQ(2U, vertices.size());
VerticesContain(vertices, Point(50, 50 * kAspectRatio));
VerticesContain(vertices, Point(100 / kAspectRatio, 100));
EXPECT_TRUE(AreValidVertices(set, vertices));
set = ResolutionSet(50, 100, 50, 100, kAspectRatio, HUGE_VAL);
vertices = set.ComputeVertices();
EXPECT_EQ(3U, vertices.size());
VerticesContain(vertices, Point(50, 50 * kAspectRatio));
VerticesContain(vertices, Point(100 / kAspectRatio, 100));
VerticesContain(vertices, Point(50, 100));
EXPECT_TRUE(AreValidVertices(set, vertices));
set = ResolutionSet(50, 100, 50, 100, 0.0, kAspectRatio);
vertices = set.ComputeVertices();
EXPECT_EQ(5U, vertices.size());
VerticesContain(vertices, Point(50, 50 * kAspectRatio));
VerticesContain(vertices, Point(100 / kAspectRatio, 100));
VerticesContain(vertices, Point(50, 50));
VerticesContain(vertices, Point(100, 100));
VerticesContain(vertices, Point(100, 50));
EXPECT_TRUE(AreValidVertices(set, vertices));
}
// An aspect ratio constraint crosses the bottom and top sides of the box.
{
const double kAspectRatio = 75.0 / 50.0;
ResolutionSet set(0, 100, 50, 100, kAspectRatio, kAspectRatio);
auto vertices = set.ComputeVertices();
EXPECT_EQ(2U, vertices.size());
VerticesContain(vertices, Point(50 / kAspectRatio, 50));
VerticesContain(vertices, Point(100 / kAspectRatio, 100));
EXPECT_TRUE(AreValidVertices(set, vertices));
set = ResolutionSet(0, 100, 50, 100, kAspectRatio, HUGE_VAL);
vertices = set.ComputeVertices();
EXPECT_EQ(4U, vertices.size());
VerticesContain(vertices, Point(50 / kAspectRatio, 50));
VerticesContain(vertices, Point(100 / kAspectRatio, 100));
VerticesContain(vertices, Point(0, 50));
VerticesContain(vertices, Point(0, 100));
EXPECT_TRUE(AreValidVertices(set, vertices));
set = ResolutionSet(0, 100, 50, 100, 0.0, kAspectRatio);
vertices = set.ComputeVertices();
EXPECT_EQ(4U, vertices.size());
VerticesContain(vertices, Point(50 / kAspectRatio, 50));
VerticesContain(vertices, Point(100 / kAspectRatio, 100));
VerticesContain(vertices, Point(100, 50));
VerticesContain(vertices, Point(100, 100));
EXPECT_TRUE(AreValidVertices(set, vertices));
}
// An aspect-ratio constraint crosses the left and right sides of the box.
{
const double kAspectRatio = 75.0 / 50.0;
ResolutionSet set(50, 100, 0, 200, kAspectRatio, kAspectRatio);
auto vertices = set.ComputeVertices();
// This one fails if floating-point precision is too high.
EXPECT_EQ(2U, vertices.size());
VerticesContain(vertices, Point(50, 50 * kAspectRatio));
VerticesContain(vertices, Point(100, 100 * kAspectRatio));
EXPECT_TRUE(AreValidVertices(set, vertices));
set = ResolutionSet(50, 100, 0, 200, kAspectRatio, HUGE_VAL);
vertices = set.ComputeVertices();
// This one fails if floating-point precision is too high.
EXPECT_EQ(4U, vertices.size());
VerticesContain(vertices, Point(50, 50 * kAspectRatio));
VerticesContain(vertices, Point(100, 100 * kAspectRatio));
VerticesContain(vertices, Point(50, 200));
VerticesContain(vertices, Point(100, 200));
EXPECT_TRUE(AreValidVertices(set, vertices));
set = ResolutionSet(50, 100, 0, 200, 0.0, kAspectRatio);
vertices = set.ComputeVertices();
EXPECT_EQ(4U, vertices.size());
VerticesContain(vertices, Point(50, 50 * kAspectRatio));
VerticesContain(vertices, Point(100, 100 * kAspectRatio));
VerticesContain(vertices, Point(50, 0));
VerticesContain(vertices, Point(100, 0));
EXPECT_TRUE(AreValidVertices(set, vertices));
}
// Aspect-ratio lines touch the corners of the box.
{
ResolutionSet set(50, 100, 50, 100, 0.5, 2.0);
auto vertices = set.ComputeVertices();
EXPECT_EQ(4U, vertices.size());
VerticesContain(vertices, Point(50, 50));
VerticesContain(vertices, Point(100, 50));
VerticesContain(vertices, Point(50, 100));
VerticesContain(vertices, Point(100, 100));
EXPECT_TRUE(AreValidVertices(set, vertices));
}
// Hexagons.
{
ResolutionSet set(10, 100, 10, 100, 0.5, 1.5);
auto vertices = set.ComputeVertices();
EXPECT_EQ(6U, vertices.size());
VerticesContain(vertices, Point(10, 10));
VerticesContain(vertices, Point(100, 100));
VerticesContain(vertices, Point(10, 10 * 1.5));
VerticesContain(vertices, Point(100 / 1.5, 100));
VerticesContain(vertices, Point(10 / 0.5, 10));
VerticesContain(vertices, Point(100, 100 * 0.5));
EXPECT_TRUE(AreValidVertices(set, vertices));
set = ResolutionSet(50, 100, 50, 100, 50.0 / 75.0, 75.0 / 50.0);
vertices = set.ComputeVertices();
EXPECT_EQ(6U, vertices.size());
VerticesContain(vertices, Point(50, 50));
VerticesContain(vertices, Point(100, 100));
VerticesContain(vertices, Point(75, 50));
VerticesContain(vertices, Point(50, 75));
VerticesContain(vertices, Point(100, 100.0 * 50.0 / 75.0));
VerticesContain(vertices, Point(100 * 50.0 / 75.0, 100.0));
EXPECT_TRUE(AreValidVertices(set, vertices));
}
// Both aspect-ratio constraints cross the left and top sides of the box.
{
ResolutionSet set(10, 100, 10, 100, 1.5, 1.7);
auto vertices = set.ComputeVertices();
EXPECT_EQ(4U, vertices.size());
VerticesContain(vertices, Point(10, 10 * 1.5));
VerticesContain(vertices, Point(10, 10 * 1.7));
VerticesContain(vertices, Point(100 / 1.5, 100));
VerticesContain(vertices, Point(100 / 1.7, 100));
EXPECT_TRUE(AreValidVertices(set, vertices));
}
// Both aspect-ratio constraints cross the left and right sides of the box.
{
ResolutionSet set(10, 100, 10, ResolutionSet::kMaxDimension, 1.5, 1.7);
auto vertices = set.ComputeVertices();
EXPECT_EQ(4U, vertices.size());
VerticesContain(vertices, Point(10, 10 * 1.5));
VerticesContain(vertices, Point(10, 10 * 1.7));
VerticesContain(vertices, Point(100, 100 * 1.5));
VerticesContain(vertices, Point(100, 100 * 1.7));
EXPECT_TRUE(AreValidVertices(set, vertices));
}
// Both aspect-ratio constraints cross the bottom and top sides of the box.
{
ResolutionSet set(10, 100, 50, 100, 2.0, 4.0);
auto vertices = set.ComputeVertices();
EXPECT_EQ(4U, vertices.size());
VerticesContain(vertices, Point(50 / 2.0, 50));
VerticesContain(vertices, Point(100 / 2.0, 100));
VerticesContain(vertices, Point(50 / 4.0, 50));
VerticesContain(vertices, Point(100 / 4.0, 100));
EXPECT_TRUE(AreValidVertices(set, vertices));
}
// Both aspect-ratio constraints cross the bottom and right sides of the box.
{
ResolutionSet set(10, 100, 50, 100, 0.7, 0.9);
auto vertices = set.ComputeVertices();
EXPECT_EQ(4U, vertices.size());
VerticesContain(vertices, Point(50 / 0.7, 50));
VerticesContain(vertices, Point(50 / 0.9, 50));
VerticesContain(vertices, Point(100, 100 * 0.7));
VerticesContain(vertices, Point(100, 100 * 0.9));
EXPECT_TRUE(AreValidVertices(set, vertices));
}
// Pentagons.
{
ResolutionSet set(10, 100, 50, 100, 0.7, 4.0);
auto vertices = set.ComputeVertices();
EXPECT_EQ(5U, vertices.size());
VerticesContain(vertices, Point(50 / 0.7, 50));
VerticesContain(vertices, Point(100, 100 * 0.7));
VerticesContain(vertices, Point(50 / 4.0, 50));
VerticesContain(vertices, Point(100 / 4.0, 100));
VerticesContain(vertices, Point(100, 100));
EXPECT_TRUE(AreValidVertices(set, vertices));
set = ResolutionSet(50, 100, 10, 100, 0.7, 1.5);
vertices = set.ComputeVertices();
EXPECT_EQ(5U, vertices.size());
VerticesContain(vertices, Point(50, 50 * 0.7));
VerticesContain(vertices, Point(100, 100 * 0.7));
VerticesContain(vertices, Point(50, 50 * 1.5));
VerticesContain(vertices, Point(100 / 1.5, 100));
VerticesContain(vertices, Point(100, 100));
EXPECT_TRUE(AreValidVertices(set, vertices));
}
// Extreme aspect ratios, for completeness.
{
ResolutionSet set(0, 100, 0, ResolutionSet::kMaxDimension, 0.0, 0.0);
auto vertices = set.ComputeVertices();
EXPECT_EQ(2U, vertices.size());
VerticesContain(vertices, Point(0, 0));
VerticesContain(vertices, Point(100, 0));
EXPECT_TRUE(AreValidVertices(set, vertices));
set = ResolutionSet(0, ResolutionSet::kMaxDimension, 0, 100, HUGE_VAL,
HUGE_VAL);
vertices = set.ComputeVertices();
EXPECT_EQ(2U, vertices.size());
VerticesContain(vertices, Point(0, 0));
VerticesContain(vertices, Point(0, 100));
EXPECT_TRUE(AreValidVertices(set, vertices));
}
}
TEST_F(MediaStreamConstraintsUtilSetsTest, NumericRangeSetDouble) {
using DoubleRangeSet = NumericRangeSet<double>;
// Set with maximum supported range.
DoubleRangeSet set;
EXPECT_EQ(0.0, set.Min());
EXPECT_EQ(HUGE_VAL, set.Max());
EXPECT_FALSE(set.IsEmpty());
// Constrained set.
const double kMin = 1.0;
const double kMax = 10.0;
set = DoubleRangeSet(kMin, kMax);
EXPECT_EQ(kMin, set.Min());
EXPECT_EQ(kMax, set.Max());
EXPECT_FALSE(set.IsEmpty());
// Empty set.
set = DoubleRangeSet(kMax, kMin);
EXPECT_TRUE(set.IsEmpty());
// Intersection.
set = DoubleRangeSet(kMin, kMax);
const double kMin2 = 5.0;
const double kMax2 = 20.0;
auto intersection = set.Intersection(DoubleRangeSet(kMin2, kMax2));
EXPECT_EQ(kMin2, intersection.Min());
EXPECT_EQ(kMax, intersection.Max());
EXPECT_FALSE(intersection.IsEmpty());
// Empty intersection.
intersection = set.Intersection(DoubleRangeSet(kMax + 1, HUGE_VAL));
EXPECT_TRUE(intersection.IsEmpty());
}
TEST_F(MediaStreamConstraintsUtilSetsTest, DiscreteSetString) {
// Universal set.
using StringSet = DiscreteSet<std::string>;
StringSet set = StringSet::UniversalSet();
EXPECT_TRUE(set.Contains("arbitrary"));
EXPECT_TRUE(set.Contains("strings"));
EXPECT_FALSE(set.IsEmpty());
EXPECT_TRUE(set.is_universal());
EXPECT_FALSE(set.HasExplicitElements());
// Constrained set.
set = StringSet(std::vector<std::string>({"a", "b", "c"}));
EXPECT_TRUE(set.Contains("a"));
EXPECT_TRUE(set.Contains("b"));
EXPECT_TRUE(set.Contains("c"));
EXPECT_FALSE(set.Contains("d"));
EXPECT_FALSE(set.IsEmpty());
EXPECT_FALSE(set.is_universal());
EXPECT_TRUE(set.HasExplicitElements());
EXPECT_EQ(std::string("a"), set.FirstElement());
// Empty set.
set = StringSet::EmptySet();
EXPECT_FALSE(set.Contains("a"));
EXPECT_FALSE(set.Contains("b"));
EXPECT_TRUE(set.IsEmpty());
EXPECT_FALSE(set.is_universal());
EXPECT_FALSE(set.HasExplicitElements());
// Intersection.
set = StringSet(std::vector<std::string>({"a", "b", "c"}));
StringSet set2 = StringSet(std::vector<std::string>({"b", "c", "d"}));
auto intersection = set.Intersection(set2);
EXPECT_FALSE(intersection.Contains("a"));
EXPECT_TRUE(intersection.Contains("b"));
EXPECT_TRUE(intersection.Contains("c"));
EXPECT_FALSE(intersection.Contains("d"));
EXPECT_FALSE(intersection.IsEmpty());
EXPECT_FALSE(intersection.is_universal());
EXPECT_TRUE(intersection.HasExplicitElements());
EXPECT_EQ(std::string("b"), intersection.FirstElement());
// Empty intersection.
set2 = StringSet(std::vector<std::string>({"d", "e", "f"}));
intersection = set.Intersection(set2);
EXPECT_FALSE(intersection.Contains("a"));
EXPECT_FALSE(intersection.Contains("b"));
EXPECT_FALSE(intersection.Contains("c"));
EXPECT_FALSE(intersection.Contains("d"));
EXPECT_TRUE(intersection.IsEmpty());
EXPECT_FALSE(intersection.is_universal());
EXPECT_FALSE(intersection.HasExplicitElements());
}
TEST_F(MediaStreamConstraintsUtilSetsTest, DiscreteSetBool) {
using BoolSet = DiscreteSet<bool>;
// Universal set.
BoolSet set = BoolSet::UniversalSet();
EXPECT_TRUE(set.Contains(true));
EXPECT_TRUE(set.Contains(false));
EXPECT_FALSE(set.IsEmpty());
EXPECT_TRUE(set.is_universal());
EXPECT_FALSE(set.HasExplicitElements());
// Constrained set.
set = BoolSet({true});
EXPECT_TRUE(set.Contains(true));
EXPECT_FALSE(set.Contains(false));
EXPECT_FALSE(set.IsEmpty());
EXPECT_FALSE(set.is_universal());
EXPECT_TRUE(set.HasExplicitElements());
EXPECT_TRUE(set.FirstElement());
set = BoolSet({false});
EXPECT_FALSE(set.Contains(true));
EXPECT_TRUE(set.Contains(false));
EXPECT_FALSE(set.IsEmpty());
EXPECT_FALSE(set.is_universal());
EXPECT_TRUE(set.HasExplicitElements());
EXPECT_FALSE(set.FirstElement());
// Empty set.
set = BoolSet::EmptySet();
EXPECT_FALSE(set.Contains(true));
EXPECT_FALSE(set.Contains(false));
EXPECT_TRUE(set.IsEmpty());
EXPECT_FALSE(set.is_universal());
EXPECT_FALSE(set.HasExplicitElements());
// Intersection.
set = BoolSet::UniversalSet();
auto intersection = set.Intersection(BoolSet({true}));
EXPECT_TRUE(intersection.Contains(true));
EXPECT_FALSE(intersection.Contains(false));
intersection = set.Intersection(set);
EXPECT_TRUE(intersection.Contains(true));
EXPECT_TRUE(intersection.Contains(true));
// Empty intersection.
set = BoolSet({true});
intersection = set.Intersection(BoolSet({false}));
EXPECT_TRUE(intersection.IsEmpty());
}
} // namespace content