blob: 64689e3a652698fcf63a6242f54370c02bfae61a [file] [log] [blame]
// Copyright 2016 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 "third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
namespace {
#define TEST_OPPORTUNITY(opp, expected_start_offset, expected_end_offset) \
EXPECT_EQ(expected_start_offset, opp.rect.start_offset); \
EXPECT_EQ(expected_end_offset, opp.rect.end_offset)
// Tests that an empty exclusion space returns exactly one layout opportunity
// each one, and sized appropriately given the area.
TEST(NGExclusionSpaceTest, Empty) {
NGExclusionSpace exclusion_space;
LayoutOpportunityVector opportunites = exclusion_space.AllLayoutOpportunities(
/* offset */ {LayoutUnit(), LayoutUnit()},
/* available_size */ LayoutUnit(100));
EXPECT_EQ(1u, opportunites.size());
TEST_OPPORTUNITY(opportunites[0], NGBfcOffset(LayoutUnit(), LayoutUnit()),
NGBfcOffset(LayoutUnit(100), LayoutUnit::Max()));
opportunites = exclusion_space.AllLayoutOpportunities(
/* offset */ {LayoutUnit(-30), LayoutUnit(-100)},
/* available_size */ LayoutUnit(50));
EXPECT_EQ(1u, opportunites.size());
TEST_OPPORTUNITY(opportunites[0],
NGBfcOffset(LayoutUnit(-30), LayoutUnit(-100)),
NGBfcOffset(LayoutUnit(20), LayoutUnit::Max()));
opportunites = exclusion_space.AllLayoutOpportunities(
/* offset */ {LayoutUnit(30), LayoutUnit(100)},
/* available_size */ LayoutUnit(50));
EXPECT_EQ(1u, opportunites.size());
TEST_OPPORTUNITY(opportunites[0],
NGBfcOffset(LayoutUnit(30), LayoutUnit(100)),
NGBfcOffset(LayoutUnit(80), LayoutUnit::Max()));
}
TEST(NGExclusionSpaceTest, SingleExclusion) {
NGExclusionSpace exclusion_space;
exclusion_space.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(10), LayoutUnit(20)),
NGBfcOffset(LayoutUnit(60), LayoutUnit(90))),
EFloat::kLeft));
LayoutOpportunityVector opportunites = exclusion_space.AllLayoutOpportunities(
/* offset */ {LayoutUnit(), LayoutUnit()},
/* available_size */ LayoutUnit(100));
EXPECT_EQ(3u, opportunites.size());
TEST_OPPORTUNITY(opportunites[0], NGBfcOffset(LayoutUnit(), LayoutUnit()),
NGBfcOffset(LayoutUnit(100), LayoutUnit(20)));
TEST_OPPORTUNITY(opportunites[1], NGBfcOffset(LayoutUnit(60), LayoutUnit()),
NGBfcOffset(LayoutUnit(100), LayoutUnit::Max()));
TEST_OPPORTUNITY(opportunites[2], NGBfcOffset(LayoutUnit(), LayoutUnit(90)),
NGBfcOffset(LayoutUnit(100), LayoutUnit::Max()));
opportunites = exclusion_space.AllLayoutOpportunities(
/* offset */ {LayoutUnit(-10), LayoutUnit(-100)},
/* available_size */ LayoutUnit(100));
EXPECT_EQ(3u, opportunites.size());
TEST_OPPORTUNITY(opportunites[0],
NGBfcOffset(LayoutUnit(-10), LayoutUnit(-100)),
NGBfcOffset(LayoutUnit(90), LayoutUnit(20)));
TEST_OPPORTUNITY(opportunites[1],
NGBfcOffset(LayoutUnit(60), LayoutUnit(-100)),
NGBfcOffset(LayoutUnit(90), LayoutUnit::Max()));
TEST_OPPORTUNITY(opportunites[2],
NGBfcOffset(LayoutUnit(-10), LayoutUnit(90)),
NGBfcOffset(LayoutUnit(90), LayoutUnit::Max()));
// This will still produce three opportunities, with a zero-width RHS
// opportunity.
opportunites = exclusion_space.AllLayoutOpportunities(
/* offset */ {LayoutUnit(10), LayoutUnit(10)},
/* available_size */ LayoutUnit(50));
EXPECT_EQ(3u, opportunites.size());
TEST_OPPORTUNITY(opportunites[0], NGBfcOffset(LayoutUnit(10), LayoutUnit(10)),
NGBfcOffset(LayoutUnit(60), LayoutUnit(20)));
TEST_OPPORTUNITY(opportunites[1], NGBfcOffset(LayoutUnit(60), LayoutUnit(10)),
NGBfcOffset(LayoutUnit(60), LayoutUnit::Max()));
TEST_OPPORTUNITY(opportunites[2], NGBfcOffset(LayoutUnit(10), LayoutUnit(90)),
NGBfcOffset(LayoutUnit(60), LayoutUnit::Max()));
// This will also produce three opportunities, as the RHS opportunity outside
// the search area creates a zero-width opportunity.
opportunites = exclusion_space.AllLayoutOpportunities(
/* offset */ {LayoutUnit(10), LayoutUnit(10)},
/* available_size */ LayoutUnit(49));
EXPECT_EQ(3u, opportunites.size());
TEST_OPPORTUNITY(opportunites[0], NGBfcOffset(LayoutUnit(10), LayoutUnit(10)),
NGBfcOffset(LayoutUnit(59), LayoutUnit(20)));
TEST_OPPORTUNITY(opportunites[1], NGBfcOffset(LayoutUnit(60), LayoutUnit(10)),
NGBfcOffset(LayoutUnit(60), LayoutUnit::Max()));
TEST_OPPORTUNITY(opportunites[2], NGBfcOffset(LayoutUnit(10), LayoutUnit(90)),
NGBfcOffset(LayoutUnit(59), LayoutUnit::Max()));
}
TEST(NGExclusionSpaceTest, TwoExclusions) {
NGExclusionSpace exclusion_space;
exclusion_space.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(), LayoutUnit()),
NGBfcOffset(LayoutUnit(150), LayoutUnit(75))),
EFloat::kLeft));
exclusion_space.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(100), LayoutUnit(75)),
NGBfcOffset(LayoutUnit(400), LayoutUnit(150))),
EFloat::kRight));
LayoutOpportunityVector opportunites = exclusion_space.AllLayoutOpportunities(
/* offset */ {LayoutUnit(), LayoutUnit()},
/* available_size */ LayoutUnit(400));
EXPECT_EQ(3u, opportunites.size());
TEST_OPPORTUNITY(opportunites[0], NGBfcOffset(LayoutUnit(150), LayoutUnit()),
NGBfcOffset(LayoutUnit(400), LayoutUnit(75)));
TEST_OPPORTUNITY(opportunites[1], NGBfcOffset(LayoutUnit(), LayoutUnit(75)),
NGBfcOffset(LayoutUnit(100), LayoutUnit::Max()));
TEST_OPPORTUNITY(opportunites[2], NGBfcOffset(LayoutUnit(), LayoutUnit(150)),
NGBfcOffset(LayoutUnit(400), LayoutUnit::Max()));
}
// Tests the "solid edge" behaviour. When "NEW" is added a new layout
// opportunity shouldn't be created above it.
//
// NOTE: This is the same example given in the code.
//
// 0 1 2 3 4 5 6 7 8
// 0 +---+ X----X+---+
// |xxx| . |xxx|
// 10 |xxx| . |xxx|
// +---+ . +---+
// 20 . .
// +---+. .+---+
// 30 |xxx| |NEW|
// |xxx| +---+
// 40 +---+
TEST(NGExclusionSpaceTest, SolidEdges) {
NGExclusionSpace exclusion_space;
exclusion_space.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(), LayoutUnit()),
NGBfcOffset(LayoutUnit(20), LayoutUnit(15))),
EFloat::kLeft));
exclusion_space.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(65), LayoutUnit()),
NGBfcOffset(LayoutUnit(85), LayoutUnit(15))),
EFloat::kRight));
exclusion_space.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(10), LayoutUnit(25)),
NGBfcOffset(LayoutUnit(30), LayoutUnit(40))),
EFloat::kLeft));
exclusion_space.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(50), LayoutUnit(25)),
NGBfcOffset(LayoutUnit(70), LayoutUnit(35))),
EFloat::kRight));
LayoutOpportunityVector opportunites = exclusion_space.AllLayoutOpportunities(
/* offset */ {LayoutUnit(), LayoutUnit()},
/* available_size */ LayoutUnit(80));
EXPECT_EQ(5u, opportunites.size());
TEST_OPPORTUNITY(opportunites[0], NGBfcOffset(LayoutUnit(20), LayoutUnit()),
NGBfcOffset(LayoutUnit(65), LayoutUnit(25)));
TEST_OPPORTUNITY(opportunites[1], NGBfcOffset(LayoutUnit(30), LayoutUnit()),
NGBfcOffset(LayoutUnit(50), LayoutUnit::Max()));
TEST_OPPORTUNITY(opportunites[2], NGBfcOffset(LayoutUnit(), LayoutUnit(15)),
NGBfcOffset(LayoutUnit(80), LayoutUnit(25)));
TEST_OPPORTUNITY(opportunites[3], NGBfcOffset(LayoutUnit(30), LayoutUnit(35)),
NGBfcOffset(LayoutUnit(80), LayoutUnit::Max()));
TEST_OPPORTUNITY(opportunites[4], NGBfcOffset(LayoutUnit(), LayoutUnit(40)),
NGBfcOffset(LayoutUnit(80), LayoutUnit::Max()));
}
// Tests that if a new exclusion doesn't overlap with a shelf, we don't add a
// new layout opportunity.
//
// NOTE: This is the same example given in the code.
//
// 0 1 2 3 4 5 6 7 8
// 0 +---+X------X+---+
// |xxx| |xxx|
// 10 |xxx| |xxx|
// +---+ +---+
// 20
// +---+
// 30 |NEW|
// +---+
TEST(NGExclusionSpaceTest, OverlappingWithShelf) {
NGExclusionSpace exclusion_space;
exclusion_space.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(), LayoutUnit()),
NGBfcOffset(LayoutUnit(20), LayoutUnit(15))),
EFloat::kLeft));
exclusion_space.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(65), LayoutUnit()),
NGBfcOffset(LayoutUnit(85), LayoutUnit(15))),
EFloat::kRight));
exclusion_space.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(70), LayoutUnit(25)),
NGBfcOffset(LayoutUnit(90), LayoutUnit(35))),
EFloat::kRight));
LayoutOpportunityVector opportunites = exclusion_space.AllLayoutOpportunities(
/* offset */ {LayoutUnit(), LayoutUnit()},
/* available_size */ LayoutUnit(80));
EXPECT_EQ(4u, opportunites.size());
TEST_OPPORTUNITY(opportunites[0], NGBfcOffset(LayoutUnit(20), LayoutUnit()),
NGBfcOffset(LayoutUnit(65), LayoutUnit::Max()));
TEST_OPPORTUNITY(opportunites[1], NGBfcOffset(LayoutUnit(), LayoutUnit(15)),
NGBfcOffset(LayoutUnit(80), LayoutUnit(25)));
TEST_OPPORTUNITY(opportunites[2], NGBfcOffset(LayoutUnit(), LayoutUnit(15)),
NGBfcOffset(LayoutUnit(70), LayoutUnit::Max()));
TEST_OPPORTUNITY(opportunites[3], NGBfcOffset(LayoutUnit(), LayoutUnit(35)),
NGBfcOffset(LayoutUnit(80), LayoutUnit::Max()));
}
// Tests that a shelf is properly inserted between two other shelves.
//
// Additionally tests that an inserted exclusion is correctly inserted in a
// shelve's line_left_edges/line_right_edges list.
//
// NOTE: This is the same example given in the code.
//
// 0 1 2 3 4 5 6 7 8
// 0 +-----+X----X+---+
// |xxxxx| |xxx|
// 10 +-----+ |xxx|
// +---+ |xxx|
// 20 |NEW| |xxx|
// X-----------X|xxx|
// 30 |xxx|
// X----------------X
TEST(NGExclusionSpaceTest, InsertBetweenShelves) {
NGExclusionSpace exclusion_space;
exclusion_space.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(), LayoutUnit()),
NGBfcOffset(LayoutUnit(30), LayoutUnit(10))),
EFloat::kLeft));
exclusion_space.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(65), LayoutUnit()),
NGBfcOffset(LayoutUnit(85), LayoutUnit(35))),
EFloat::kRight));
exclusion_space.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(10), LayoutUnit(15)),
NGBfcOffset(LayoutUnit(30), LayoutUnit(25))),
EFloat::kLeft));
LayoutOpportunityVector opportunites = exclusion_space.AllLayoutOpportunities(
/* offset */ {LayoutUnit(30), LayoutUnit(15)},
/* available_size */ LayoutUnit(30));
// NOTE: This demonstrates a quirk when querying the exclusion space for
// opportunities. The exclusion space may return multiple exclusions of
// exactly the same (or growing) size. This quirk still produces correct
// results for code which uses it as the exclusions grow or keep the same
// size.
EXPECT_EQ(3u, opportunites.size());
TEST_OPPORTUNITY(opportunites[0], NGBfcOffset(LayoutUnit(30), LayoutUnit(15)),
NGBfcOffset(LayoutUnit(60), LayoutUnit::Max()));
TEST_OPPORTUNITY(opportunites[1], NGBfcOffset(LayoutUnit(30), LayoutUnit(25)),
NGBfcOffset(LayoutUnit(60), LayoutUnit::Max()));
TEST_OPPORTUNITY(opportunites[2], NGBfcOffset(LayoutUnit(30), LayoutUnit(35)),
NGBfcOffset(LayoutUnit(60), LayoutUnit::Max()));
}
TEST(NGExclusionSpaceTest, ZeroInlineSizeOpportunity) {
NGExclusionSpace exclusion_space;
exclusion_space.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(), LayoutUnit()),
NGBfcOffset(LayoutUnit(100), LayoutUnit(10))),
EFloat::kLeft));
LayoutOpportunityVector opportunites = exclusion_space.AllLayoutOpportunities(
/* offset */ {LayoutUnit(), LayoutUnit()},
/* available_size */ LayoutUnit(100));
EXPECT_EQ(2u, opportunites.size());
TEST_OPPORTUNITY(opportunites[0], NGBfcOffset(LayoutUnit(100), LayoutUnit()),
NGBfcOffset(LayoutUnit(100), LayoutUnit::Max()));
TEST_OPPORTUNITY(opportunites[1], NGBfcOffset(LayoutUnit(), LayoutUnit(10)),
NGBfcOffset(LayoutUnit(100), LayoutUnit::Max()));
}
TEST(NGExclusionSpaceTest, NegativeInlineSizeOpportunityLeft) {
NGExclusionSpace exclusion_space;
exclusion_space.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(), LayoutUnit()),
NGBfcOffset(LayoutUnit(120), LayoutUnit(10))),
EFloat::kLeft));
LayoutOpportunityVector opportunites = exclusion_space.AllLayoutOpportunities(
/* offset */ {LayoutUnit(), LayoutUnit()},
/* available_size */ LayoutUnit(100));
EXPECT_EQ(2u, opportunites.size());
TEST_OPPORTUNITY(opportunites[0], NGBfcOffset(LayoutUnit(120), LayoutUnit()),
NGBfcOffset(LayoutUnit(120), LayoutUnit::Max()));
TEST_OPPORTUNITY(opportunites[1], NGBfcOffset(LayoutUnit(), LayoutUnit(10)),
NGBfcOffset(LayoutUnit(100), LayoutUnit::Max()));
}
TEST(NGExclusionSpaceTest, NegativeInlineSizeOpportunityRight) {
NGExclusionSpace exclusion_space;
exclusion_space.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(-20), LayoutUnit()),
NGBfcOffset(LayoutUnit(100), LayoutUnit(10))),
EFloat::kRight));
LayoutOpportunityVector opportunites = exclusion_space.AllLayoutOpportunities(
/* offset */ {LayoutUnit(), LayoutUnit()},
/* available_size */ LayoutUnit(100));
EXPECT_EQ(2u, opportunites.size());
TEST_OPPORTUNITY(opportunites[0], NGBfcOffset(LayoutUnit(), LayoutUnit()),
NGBfcOffset(LayoutUnit(), LayoutUnit::Max()));
TEST_OPPORTUNITY(opportunites[1], NGBfcOffset(LayoutUnit(), LayoutUnit(10)),
NGBfcOffset(LayoutUnit(100), LayoutUnit::Max()));
}
TEST(NGExclusionSpaceTest, PreInitialization) {
NGExclusionSpace original_exclusion_space;
original_exclusion_space.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(), LayoutUnit()),
NGBfcOffset(LayoutUnit(20), LayoutUnit(15))),
EFloat::kLeft));
original_exclusion_space.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(65), LayoutUnit()),
NGBfcOffset(LayoutUnit(85), LayoutUnit(15))),
EFloat::kRight));
NGExclusionSpace exclusion_space1;
exclusion_space1.PreInitialize(original_exclusion_space);
EXPECT_NE(original_exclusion_space, exclusion_space1);
exclusion_space1.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(), LayoutUnit()),
NGBfcOffset(LayoutUnit(20), LayoutUnit(15))),
EFloat::kLeft));
EXPECT_NE(original_exclusion_space, exclusion_space1);
// Adding the same exclusions will make the spaces equal.
exclusion_space1.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(65), LayoutUnit()),
NGBfcOffset(LayoutUnit(85), LayoutUnit(15))),
EFloat::kRight));
EXPECT_EQ(original_exclusion_space, exclusion_space1);
// Adding a third exclusion will make the spaces non-equal.
exclusion_space1.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(10), LayoutUnit(25)),
NGBfcOffset(LayoutUnit(30), LayoutUnit(40))),
EFloat::kLeft));
EXPECT_NE(original_exclusion_space, exclusion_space1);
NGExclusionSpace exclusion_space2;
exclusion_space2.PreInitialize(original_exclusion_space);
EXPECT_NE(original_exclusion_space, exclusion_space2);
exclusion_space2.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(), LayoutUnit()),
NGBfcOffset(LayoutUnit(20), LayoutUnit(15))),
EFloat::kLeft));
EXPECT_NE(original_exclusion_space, exclusion_space2);
// Adding a different second exclusion will make the spaces non-equal.
exclusion_space2.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(10), LayoutUnit(25)),
NGBfcOffset(LayoutUnit(30), LayoutUnit(40))),
EFloat::kLeft));
EXPECT_NE(original_exclusion_space, exclusion_space2);
}
TEST(NGExclusionSpaceTest, MergeExclusionSpacesNoPreviousExclusions) {
NGExclusionSpace old_input;
NGExclusionSpace old_output = old_input;
old_output.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(10), LayoutUnit(25)),
NGBfcOffset(LayoutUnit(30), LayoutUnit(40))),
EFloat::kLeft));
NGExclusionSpace new_input;
NGExclusionSpace new_output = NGExclusionSpace::MergeExclusionSpaces(
old_output, old_input, new_input,
/* offset_delta */ {LayoutUnit(10), LayoutUnit(20)});
// To check the equality pre-initialize a new exclusion space with the
// |new_output|, and add the expected exclusions.
NGExclusionSpace expected;
expected.PreInitialize(new_output);
expected.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(20), LayoutUnit(45)),
NGBfcOffset(LayoutUnit(40), LayoutUnit(60))),
EFloat::kLeft));
EXPECT_EQ(expected, new_output);
}
TEST(NGExclusionSpaceTest, MergeExclusionSpacesPreviousExclusions) {
NGExclusionSpace old_input;
old_input.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(20), LayoutUnit(45)),
NGBfcOffset(LayoutUnit(40), LayoutUnit(60))),
EFloat::kLeft));
NGExclusionSpace old_output = old_input;
old_output.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(100), LayoutUnit(45)),
NGBfcOffset(LayoutUnit(140), LayoutUnit(60))),
EFloat::kRight));
NGExclusionSpace new_input;
new_input.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(20), LayoutUnit(45)),
NGBfcOffset(LayoutUnit(40), LayoutUnit(50))),
EFloat::kLeft));
NGExclusionSpace new_output = NGExclusionSpace::MergeExclusionSpaces(
old_output, old_input, new_input,
/* offset_delta */ {LayoutUnit(10), LayoutUnit(20)});
// To check the equality pre-initialize a new exclusion space with the
// |new_output|, and add the expected exclusions.
NGExclusionSpace expected;
expected.PreInitialize(new_output);
expected.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(20), LayoutUnit(45)),
NGBfcOffset(LayoutUnit(40), LayoutUnit(50))),
EFloat::kLeft));
expected.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(110), LayoutUnit(65)),
NGBfcOffset(LayoutUnit(150), LayoutUnit(80))),
EFloat::kRight));
EXPECT_EQ(expected, new_output);
}
TEST(NGExclusionSpaceTest, MergeExclusionSpacesNoOutputExclusions) {
NGExclusionSpace old_input;
old_input.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(20), LayoutUnit(45)),
NGBfcOffset(LayoutUnit(40), LayoutUnit(60))),
EFloat::kLeft));
old_input.Add(NGExclusion::Create(
NGBfcRect(NGBfcOffset(LayoutUnit(100), LayoutUnit(45)),
NGBfcOffset(LayoutUnit(140), LayoutUnit(60))),
EFloat::kRight));
NGExclusionSpace old_output = old_input;
NGExclusionSpace new_input;
NGExclusionSpace new_output = NGExclusionSpace::MergeExclusionSpaces(
old_output, old_input, new_input,
/* offset_delta */ {LayoutUnit(10), LayoutUnit(20)});
NGExclusionSpace expected;
EXPECT_EQ(expected, new_output);
}
} // namespace
} // namespace blink