blob: 3630c0204f6d77dbc1ddd69ebf13e4a537958e3a [file] [log] [blame]
// Copyright 2018 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 "ui/gfx/rrect_f.h"
#include "base/stl_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/rrect_f_builder.h"
namespace gfx {
TEST(RRectFTest, IsEmpty) {
EXPECT_TRUE(RRectF().IsEmpty());
EXPECT_TRUE(RRectF(0, 0, 0, 0, 0).IsEmpty());
EXPECT_TRUE(RRectF(0, 0, 10, 0, 0).IsEmpty());
EXPECT_TRUE(RRectF(0, 0, 0, 10, 0).IsEmpty());
EXPECT_TRUE(RRectF(0, 0, 0, 10, 10).IsEmpty());
EXPECT_FALSE(RRectF(0, 0, 10, 10, 0).IsEmpty());
}
TEST(RRectFTest, Equals) {
EXPECT_EQ(RRectF(0, 0, 0, 0, 0, 0), RRectF(0, 0, 0, 0, 0, 0));
EXPECT_EQ(RRectF(1, 2, 3, 4, 5, 6), RRectF(1, 2, 3, 4, 5, 6));
EXPECT_EQ(RRectF(1, 2, 3, 4, 5, 5), RRectF(1, 2, 3, 4, 5));
EXPECT_EQ(RRectF(0, 0, 2, 3, 0, 0), RRectF(0, 0, 2, 3, 0, 1));
EXPECT_EQ(RRectF(0, 0, 2, 3, 0, 0), RRectF(0, 0, 2, 3, 1, 0));
EXPECT_EQ(RRectF(1, 2, 3, 0, 5, 6), RRectF(0, 0, 0, 0, 0, 0));
EXPECT_EQ(RRectF(0, 0, 0, 0, 5, 6), RRectF(0, 0, 0, 0, 0, 0));
EXPECT_NE(RRectF(10, 20, 30, 40, 7, 8), RRectF(1, 20, 30, 40, 7, 8));
EXPECT_NE(RRectF(10, 20, 30, 40, 7, 8), RRectF(10, 2, 30, 40, 7, 8));
EXPECT_NE(RRectF(10, 20, 30, 40, 7, 8), RRectF(10, 20, 3, 40, 7, 8));
EXPECT_NE(RRectF(10, 20, 30, 40, 7, 8), RRectF(10, 20, 30, 4, 7, 8));
EXPECT_NE(RRectF(10, 20, 30, 40, 7, 8), RRectF(10, 20, 30, 40, 5, 8));
EXPECT_NE(RRectF(10, 20, 30, 40, 7, 8), RRectF(10, 20, 30, 40, 7, 6));
}
TEST(RRectFTest, PlusMinusOffset) {
const RRectF a(40, 50, 60, 70, 5);
gfx::Vector2d offset(23, 34);
RRectF correct(63, 84, 60, 70, 5);
RRectF b = a + offset;
ASSERT_EQ(b, correct);
b = a;
b.Offset(offset);
ASSERT_EQ(b, correct);
correct = RRectF(17, 16, 60, 70, 5);
b = a - offset;
ASSERT_EQ(b, correct);
b = a;
b.Offset(-offset);
ASSERT_EQ(b, correct);
}
TEST(RRectFTest, RRectTypes) {
RRectF a(40, 50, 0, 70, 0);
EXPECT_EQ(a.GetType(), RRectF::Type::kEmpty);
EXPECT_TRUE(a.IsEmpty());
a = RRectF(40, 50, 60, 70, 0);
EXPECT_EQ(a.GetType(), RRectF::Type::kRect);
a = RRectF(40, 50, 60, 70, 5);
EXPECT_EQ(a.GetType(), RRectF::Type::kSingle);
a = RRectF(40, 50, 60, 70, 5, 5);
EXPECT_EQ(a.GetType(), RRectF::Type::kSingle);
a = RRectF(40, 50, 60, 70, 6, 3);
EXPECT_EQ(a.GetType(), RRectF::Type::kSimple);
a = RRectF(40, 50, 60, 70, 30, 3);
EXPECT_EQ(a.GetType(), RRectF::Type::kSimple);
a = RRectF(40, 50, 60, 70, 30, 35);
EXPECT_EQ(a.GetType(), RRectF::Type::kOval);
a.SetCornerRadii(RRectF::Corner::kLowerRight, gfx::Vector2dF(7, 8));
EXPECT_EQ(a.GetType(), RRectF::Type::kComplex);
// When one radius is larger than half its dimension, both radii are scaled
// down proportionately.
a = RRectF(40, 50, 60, 70, 30, 70);
EXPECT_EQ(a.GetType(), RRectF::Type::kSimple);
EXPECT_EQ(a, RRectF(40, 50, 60, 70, 15, 35));
// If they stay equal to half the radius, it stays oval.
a = RRectF(40, 50, 60, 70, 120, 140);
EXPECT_EQ(a.GetType(), RRectF::Type::kOval);
}
void CheckRadii(RRectF val,
float ulx,
float uly,
float urx,
float ury,
float lrx,
float lry,
float llx,
float lly) {
EXPECT_EQ(val.GetCornerRadii(RRectF::Corner::kUpperLeft),
gfx::Vector2dF(ulx, uly));
EXPECT_EQ(val.GetCornerRadii(RRectF::Corner::kUpperRight),
gfx::Vector2dF(urx, ury));
EXPECT_EQ(val.GetCornerRadii(RRectF::Corner::kLowerRight),
gfx::Vector2dF(lrx, lry));
EXPECT_EQ(val.GetCornerRadii(RRectF::Corner::kLowerLeft),
gfx::Vector2dF(llx, lly));
}
TEST(RRectFTest, RRectRadii) {
RRectF a(40, 50, 60, 70, 0);
CheckRadii(a, 0, 0, 0, 0, 0, 0, 0, 0);
a.SetCornerRadii(RRectF::Corner::kUpperLeft, 1, 2);
CheckRadii(a, 1, 2, 0, 0, 0, 0, 0, 0);
a.SetCornerRadii(RRectF::Corner::kUpperRight, 3, 4);
CheckRadii(a, 1, 2, 3, 4, 0, 0, 0, 0);
a.SetCornerRadii(RRectF::Corner::kLowerRight, 5, 6);
CheckRadii(a, 1, 2, 3, 4, 5, 6, 0, 0);
a.SetCornerRadii(RRectF::Corner::kLowerLeft, 7, 8);
CheckRadii(a, 1, 2, 3, 4, 5, 6, 7, 8);
RRectF b(40, 50, 60, 70, 1, 2, 3, 4, 5, 6, 7, 8);
EXPECT_EQ(a, b);
}
TEST(RRectFTest, FromRectF) {
// Check that explicit conversion from float rect works.
RectF a(40, 50, 60, 70);
RRectF b(40, 50, 60, 70, 0);
RRectF c = RRectF(a);
EXPECT_EQ(b, c);
}
TEST(RRectFTest, FromSkRRect) {
// Check that explicit conversion from SkRRect works.
SkRRect a = SkRRect::MakeRectXY(SkRect::MakeXYWH(40, 50, 60, 70), 15, 25);
RRectF b(40, 50, 60, 70, 15, 25);
RRectF c = RRectF(a);
EXPECT_EQ(b, c);
// Try with single radius constructor.
a = SkRRect::MakeRectXY(SkRect::MakeXYWH(40, 50, 60, 70), 15, 15);
b = RRectF(40, 50, 60, 70, 15);
c = RRectF(a);
EXPECT_EQ(b, c);
}
TEST(RRectFTest, FromRoundedCornersF) {
constexpr RectF kRect(50.0f, 40.0f);
constexpr RoundedCornersF kCorners(1.5f, 2.5f, 3.5f, 4.5f);
const RRectF rrect_f(kRect, kCorners);
const auto upper_left = rrect_f.GetCornerRadii(RRectF::Corner::kUpperLeft);
EXPECT_EQ(kCorners.upper_left(), upper_left.x());
EXPECT_EQ(kCorners.upper_left(), upper_left.y());
const auto upper_right = rrect_f.GetCornerRadii(RRectF::Corner::kUpperRight);
EXPECT_EQ(kCorners.upper_right(), upper_right.x());
EXPECT_EQ(kCorners.upper_right(), upper_right.y());
const auto lower_right = rrect_f.GetCornerRadii(RRectF::Corner::kLowerRight);
EXPECT_EQ(kCorners.lower_right(), lower_right.x());
EXPECT_EQ(kCorners.lower_right(), lower_right.y());
const auto lower_left = rrect_f.GetCornerRadii(RRectF::Corner::kLowerLeft);
EXPECT_EQ(kCorners.lower_left(), lower_left.x());
EXPECT_EQ(kCorners.lower_left(), lower_left.y());
}
TEST(RRectFTest, ToString) {
RRectF a(40, 50, 60, 70, 0);
EXPECT_EQ(a.ToString(), "40.000,50.000 60.000x70.000, rectangular");
a = RRectF(40, 50, 60, 70, 15);
EXPECT_EQ(a.ToString(), "40.000,50.000 60.000x70.000, radius 15.000");
a = RRectF(40, 50, 60, 70, 15, 25);
EXPECT_EQ(a.ToString(),
"40.000,50.000 60.000x70.000, x_rad 15.000, y_rad 25.000");
a.SetCornerRadii(RRectF::Corner::kLowerRight, gfx::Vector2dF(7, 8));
EXPECT_EQ(a.ToString(),
"40.000,50.000 60.000x70.000, [15.000 25.000] "
"[15.000 25.000] [7.000 8.000] [15.000 25.000]");
}
TEST(RRectFTest, Sizes) {
RRectF a(40, 50, 60, 70, 5, 6);
EXPECT_EQ(a.rect().x(), 40);
EXPECT_EQ(a.rect().y(), 50);
EXPECT_EQ(a.rect().width(), 60);
EXPECT_EQ(a.rect().height(), 70);
EXPECT_EQ(a.GetSimpleRadii().x(), 5);
EXPECT_EQ(a.GetSimpleRadii().y(), 6);
a = RRectF(40, 50, 60, 70, 5, 5);
EXPECT_EQ(a.GetSimpleRadius(), 5);
a.Clear();
EXPECT_TRUE(a.IsEmpty());
// Make sure ovals can still get simple radii
a = RRectF(40, 50, 60, 70, 30, 35);
EXPECT_EQ(a.GetType(), RRectF::Type::kOval);
EXPECT_EQ(a.GetSimpleRadii().x(), 30);
EXPECT_EQ(a.GetSimpleRadii().y(), 35);
}
TEST(RRectFTest, Contains) {
RRectF a(40, 50, 60, 70, 5, 6);
RectF b(50, 60, 5, 6);
EXPECT_TRUE(a.Contains(b));
b = RectF(40, 50, 5, 6); // Right on the border
EXPECT_FALSE(a.Contains(b));
b = RectF(95, 114, 5, 6); // Right on the border
EXPECT_FALSE(a.Contains(b));
b = RectF(40, 50, 60, 70);
EXPECT_FALSE(a.Contains(b));
}
TEST(RRectFTest, Scale) {
// Note that SKRRect (the backing for RRectF) does not support scaling by NaN,
// or scaling out of numerical bounds. So this test doesn't exercise those.
static const struct Test {
float x1; // source
float y1;
float w1;
float h1;
float x_rad1;
float y_rad1;
float x_scale;
float y_scale;
float x2; // target
float y2;
float w2;
float h2;
float x_rad2;
float y_rad2;
} tests[] = {
{3.0f, 4.0f, 5.0f, 6.0f, 0.0f, 0.0f, 1.5f, 1.5f, 4.5f, 6.0f, 7.5f, 9.0f,
0.0f, 0.0f},
{3.0f, 4.0f, 5.0f, 6.0f, 1.0f, 1.0f, 1.5f, 1.5f, 4.5f, 6.0f, 7.5f, 9.0f,
1.5f, 1.5f},
{3.0f, 4.0f, 5.0f, 6.0f, 0.0f, 0.0f, 1.5f, 3.0f, 4.5f, 12.0f, 7.5f, 18.0f,
0.0f, 0.0f},
{3.0f, 4.0f, 5.0f, 6.0f, 1.0f, 1.0f, 1.5f, 3.0f, 4.5f, 12.0f, 7.5f, 18.0f,
1.5f, 3.0f},
{3.0f, 4.0f, 0.0f, 6.0f, 1.0f, 1.0f, 1.5f, 1.5f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f},
{3.0f, 4.0f, 5.0f, 6.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f},
{3.0f, 4.0f, 5.0f, 6.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f},
{3.0f, 4.0f, 5.0f, 6.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f},
};
for (size_t i = 0; i < base::size(tests); ++i) {
RRectF r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1,
tests[i].x_rad1, tests[i].y_rad1);
RRectF r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2,
tests[i].x_rad2, tests[i].y_rad2);
r1.Scale(tests[i].x_scale, tests[i].y_scale);
ASSERT_TRUE(r1.GetType() <= RRectF::Type::kSimple);
EXPECT_EQ(r1.rect().x(), r2.rect().x());
EXPECT_EQ(r1.rect().y(), r2.rect().y());
EXPECT_EQ(r1.rect().width(), r2.rect().width());
EXPECT_EQ(r1.rect().height(), r2.rect().height());
EXPECT_EQ(r1.GetSimpleRadii(), r2.GetSimpleRadii());
}
}
TEST(RRectFTest, InsetOutset) {
RRectF a(40, 50, 60, 70, 5);
RRectF b = a;
b.Inset(3);
ASSERT_EQ(b, RRectF(43, 53, 54, 64, 2));
b = a;
b.Outset(3);
ASSERT_EQ(b, RRectF(37, 47, 66, 76, 8));
}
// The following tests(started with "Build*") are for RRectFBuilder. All
// different tests are to make sure that existing RRectF definitions can be
// implemented with RRectFBuilder.
TEST(RRectFTest, BuildFromRectF) {
RectF a = RectF();
RRectF b(a);
RRectF c = RRectFBuilder().set_rect(a).Build();
EXPECT_EQ(b, c);
a = RectF(60, 70);
b = RRectF(a);
c = RRectFBuilder().set_rect(a).Build();
EXPECT_EQ(b, c);
a = RectF(40, 50, 60, 70);
b = RRectF(a);
c = RRectFBuilder().set_rect(a).Build();
EXPECT_EQ(b, c);
}
TEST(RRectFTest, BuildFromRadius) {
RRectF a(40, 50, 60, 70, 15);
RRectF b = RRectFBuilder()
.set_origin(40, 50)
.set_size(60, 70)
.set_radius(15)
.Build();
EXPECT_EQ(a, b);
a = RRectF(40, 50, 60, 70, 15, 25);
b = RRectFBuilder()
.set_origin(40, 50)
.set_size(60, 70)
.set_radius(15, 25)
.Build();
EXPECT_EQ(a, b);
const PointF p(40, 50);
const SizeF s(60, 70);
b = RRectFBuilder().set_origin(p).set_size(s).set_radius(15, 25).Build();
EXPECT_EQ(a, b);
}
TEST(RRectFTest, BuildFromRectFWithRadius) {
RectF a(40, 50, 60, 70);
RRectF b(a, 15);
RRectF c = RRectFBuilder().set_rect(a).set_radius(15).Build();
EXPECT_EQ(b, c);
b = RRectF(a, 15, 25);
c = RRectFBuilder().set_rect(a).set_radius(15, 25).Build();
EXPECT_EQ(b, c);
}
TEST(RRectFTest, BuildFromCorners) {
RRectF a(40, 50, 60, 70, 1, 2, 3, 4, 5, 6, 7, 8);
RRectF b = RRectFBuilder()
.set_origin(40, 50)
.set_size(60, 70)
.set_upper_left(1, 2)
.set_upper_right(3, 4)
.set_lower_right(5, 6)
.set_lower_left(7, 8)
.Build();
EXPECT_EQ(a, b);
}
TEST(RRectFTest, BuildFromRectFWithCorners) {
RectF a(40, 50, 60, 70);
RRectF b(a, 1, 2, 3, 4, 5, 6, 7, 8);
RRectF c = RRectFBuilder()
.set_rect(a)
.set_upper_left(1, 2)
.set_upper_right(3, 4)
.set_lower_right(5, 6)
.set_lower_left(7, 8)
.Build();
EXPECT_EQ(b, c);
}
TEST(RRectFTest, BuildFromRoundedCornersF) {
RectF a(40, 50, 60, 70);
RoundedCornersF corners(1.5f, 2.5f, 3.5f, 4.5f);
RRectF b(a, corners);
RRectF c = RRectFBuilder().set_rect(a).set_corners(corners).Build();
EXPECT_EQ(b, c);
}
// In the following tests(*CornersHigherThanSize), we test whether the corner
// radii gets truncated in case of being greater than the width/height.
TEST(RRectFTest, BuildFromCornersHigherThanSize) {
RRectF a(0, 0, 20, 10, 12, 2, 8, 4, 14, 6, 6, 8);
RRectF b = RRectFBuilder()
.set_origin(0, 0)
.set_size(20, 10)
.set_upper_left(48, 8)
.set_upper_right(32, 16)
.set_lower_right(56, 24)
.set_lower_left(24, 32)
.Build();
EXPECT_EQ(a, b);
}
TEST(RRectFTest, BuildFromRectFWithCornersHigherThanSize) {
RectF a(0, 0, 20, 10);
RRectF b(a, 12, 2, 8, 4, 14, 6, 6, 8);
RRectF c = RRectFBuilder()
.set_rect(a)
.set_upper_left(48, 8)
.set_upper_right(32, 16)
.set_lower_right(56, 24)
.set_lower_left(24, 32)
.Build();
EXPECT_EQ(b, c);
}
// In this test, we set the radius first but then change the value of the
// corners.
TEST(RRectFTest, BuildFromRadiusAndCorners) {
RRectF a(40, 50, 60, 70, 1, 2, 3, 4, 15, 25, 15, 25);
RRectF b = RRectFBuilder()
.set_origin(40, 50)
.set_size(60, 70)
.set_radius(15, 25)
.set_upper_left(1, 2)
.set_upper_right(3, 4)
.Build();
EXPECT_EQ(a, b);
}
} // namespace gfx