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

#include "ui/views/layout/flex_layout.h"

#include <stddef.h>

#include <algorithm>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/numerics/safe_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/views/border.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/flex_layout_types.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/test/test_views.h"
#include "ui/views/test/views_test_utils.h"
#include "ui/views/view.h"
#include "ui/views/view_class_properties.h"

namespace views {

namespace {

using gfx::Insets;
using gfx::Point;
using gfx::Rect;
using gfx::Size;
using std::optional;

class MockView : public View {
  METADATA_HEADER(MockView, View)

 public:
  enum class SizeMode { kUsePreferredSize, kFixedArea };

  void set_preferred_size(gfx::Size preferred_size) {
    preferred_size_ = preferred_size;
  }

  void SetMinimumSize(const Size& minimum_size) {
    minimum_size_ = minimum_size;
  }

  Size GetMinimumSize() const override {
    return minimum_size_.value_or(GetPreferredSize({}));
  }

  void SetMaximumSize(gfx::Size maximum_size) { maximum_size_ = maximum_size; }

  Size GetMaximumSize() const override { return maximum_size_; }

  gfx::Size CalculatePreferredSize(
      const SizeBounds& available_size) const override {
    gfx::Size preferred_size =
        preferred_size_ ? preferred_size_.value()
                        : View::CalculatePreferredSize(available_size);
    switch (size_mode_) {
      case SizeMode::kUsePreferredSize:
        return preferred_size;
      case SizeMode::kFixedArea: {
        int width = available_size.width().min_of(preferred_size.width());
        if (width <= 0) {
          return preferred_size;
        }

        width = std::max(width, minimum_size_ ? minimum_size_->width() : 0);
        return gfx::Size(
            width, (preferred_size.width() * preferred_size.height()) / width);
      }
    }
  }

  void set_size_mode(SizeMode size_mode) { size_mode_ = size_mode; }

  void SetVisible(bool visible) override {
    View::SetVisible(visible);
    ++set_visible_count_;
  }

  int GetSetVisibleCount() const { return set_visible_count_; }

  void ResetCounts() { set_visible_count_ = 0; }

 private:
  std::optional<gfx::Size> preferred_size_;
  optional<Size> minimum_size_;
  gfx::Size maximum_size_;
  int set_visible_count_ = 0;
  SizeMode size_mode_ = SizeMode::kUsePreferredSize;
};

BEGIN_METADATA(MockView)
ADD_PROPERTY_METADATA(gfx::Size, MaximumSize)
END_METADATA

// Custom flex rule that snaps a view between its preferred size and half that
// size in each dimension.
Size CustomFlexImpl(bool snap_to_zero,
                    const View* view,
                    const SizeBounds& maximum_size) {
  const Size large_size = view->GetPreferredSize({});
  const Size small_size = Size(large_size.width() / 2, large_size.height() / 2);
  int horizontal = 0;
  if (maximum_size.width() >= large_size.width()) {
    horizontal = large_size.width();
  } else if (maximum_size.width() >= small_size.width() || !snap_to_zero) {
    horizontal = small_size.width();
  }
  int vertical = 0;
  if (maximum_size.height() >= large_size.height()) {
    vertical = large_size.height();
  } else if (maximum_size.height() >= small_size.height() || !snap_to_zero) {
    vertical = small_size.height();
  }
  return Size(horizontal, vertical);
}

class FlexLayoutTest : public testing::Test {
 public:
  void SetUp() override {
    host_ = std::make_unique<View>();
    layout_ = host_->SetLayoutManager(std::make_unique<FlexLayout>());
  }

  MockView* AddChild(const Size& preferred_size,
                     const optional<Size>& minimum_size = optional<Size>(),
                     bool visible = true) {
    return AddChild(host_.get(), preferred_size, minimum_size, visible);
  }

  static MockView* AddChild(
      View* parent,
      const Size& preferred_size,
      const optional<Size>& minimum_size = optional<Size>(),
      bool visible = true) {
    MockView* const child = new MockView();
    child->set_preferred_size(preferred_size);
    if (minimum_size.has_value()) {
      child->SetMinimumSize(minimum_size.value());
    }
    if (!visible) {
      child->SetVisible(false);
    }
    parent->AddChildViewRaw(child);
    return child;
  }

  std::vector<Rect> GetChildBounds() const {
    std::vector<Rect> result;
    std::ranges::transform(host_->children(), std::back_inserter(result),
                           [](const View* v) {
                             return v->GetVisible() ? v->bounds() : gfx::Rect();
                           });
    return result;
  }

 protected:
  // Constants re-used in many tests.
  static constexpr Insets kSmallInsets = Insets::TLBR(1, 2, 3, 4);
  static constexpr Insets kLayoutInsets = Insets::TLBR(5, 6, 7, 9);
  static constexpr Insets kLargeInsets = Insets::TLBR(10, 11, 12, 13);
  static constexpr Size kChild1Size = Size(12, 10);
  static constexpr Size kChild2Size = Size(13, 11);
  static constexpr Size kChild3Size = Size(17, 13);

  // Use preferred size, but adjust height for width.
  static const FlexSpecification kPreferredAdjustHeight;

  // Preferred size or drop out.
  static const FlexSpecification kDropOut;
  static const FlexSpecification kDropOutHighPriority;

  // Scale from preferred down to minimum or zero.
  static const FlexSpecification kFlex1ScaleToMinimum;
  static const FlexSpecification kFlex2ScaleToMinimum;
  static const FlexSpecification kFlex1ScaleToMinimumHighPriority;
  static const FlexSpecification kFlex1ScaleToZero;

  // Scale from a minimum value up to infinity.
  static const FlexSpecification kUnbounded;
  static const FlexSpecification kUnboundedSnapToMinimum;
  static const FlexSpecification kUnboundedSnapToZero;
  static const FlexSpecification kUnboundedScaleToMinimumSnapToZero;
  static const FlexSpecification kUnboundedScaleToZero;
  static const FlexSpecification kUnboundedScaleToZeroAdjustHeight;
  static const FlexSpecification kUnboundedScaleToMinimum;
  static const FlexSpecification kUnboundedScaleToMinimumHighPriority;

  // Scale from a minimum value up to infinity, but only on the horizontal axis.
  static const FlexSpecification kUnboundedSnapToMinimumHorizontal;
  static const FlexSpecification kUnboundedScaleToMinimumSnapToZeroHorizontal;
  static const FlexSpecification kUnboundedScaleToZeroHorizontal;

  // Scale from a minimum value up to a maximum value.
  static const FlexSpecification kScaleToMaximum;

  // Custom flex which scales step-wise.
  static const FlexSpecification kCustomFlex;
  static const FlexSpecification kCustomFlexSnapToZero;

  std::unique_ptr<View> host_;
  raw_ptr<FlexLayout> layout_;
};

// static
constexpr Insets FlexLayoutTest::kSmallInsets;
constexpr Insets FlexLayoutTest::kLayoutInsets;
constexpr Insets FlexLayoutTest::kLargeInsets;
constexpr Size FlexLayoutTest::kChild1Size;
constexpr Size FlexLayoutTest::kChild2Size;
constexpr Size FlexLayoutTest::kChild3Size;

const FlexSpecification FlexLayoutTest::kPreferredAdjustHeight =
    FlexSpecification(MinimumFlexSizeRule::kPreferred,
                      MaximumFlexSizeRule::kPreferred,
                      true)
        .WithWeight(0);
const FlexSpecification FlexLayoutTest::kDropOut =
    FlexSpecification(MinimumFlexSizeRule::kPreferredSnapToZero,
                      MaximumFlexSizeRule::kPreferred)
        .WithWeight(0)
        .WithOrder(2);
const FlexSpecification FlexLayoutTest::kDropOutHighPriority =
    FlexLayoutTest::kDropOut.WithOrder(1);
const FlexSpecification FlexLayoutTest::kFlex1ScaleToZero =
    FlexSpecification(MinimumFlexSizeRule::kScaleToZero,
                      MaximumFlexSizeRule::kPreferred)
        .WithOrder(2);
const FlexSpecification FlexLayoutTest::kFlex1ScaleToMinimum =
    FlexSpecification(MinimumFlexSizeRule::kScaleToMinimum,
                      MaximumFlexSizeRule::kPreferred)
        .WithOrder(2);
const FlexSpecification FlexLayoutTest::kFlex2ScaleToMinimum =
    FlexLayoutTest::kFlex1ScaleToMinimum.WithWeight(2);
const FlexSpecification FlexLayoutTest::kFlex1ScaleToMinimumHighPriority =
    FlexLayoutTest::kFlex1ScaleToMinimum.WithOrder(1);
const FlexSpecification FlexLayoutTest::kUnbounded =
    FlexSpecification(MinimumFlexSizeRule::kPreferred,
                      MaximumFlexSizeRule::kUnbounded)
        .WithOrder(2);
const FlexSpecification FlexLayoutTest::kUnboundedSnapToMinimum =
    FlexSpecification(MinimumFlexSizeRule::kPreferredSnapToMinimum,
                      MaximumFlexSizeRule::kUnbounded)
        .WithOrder(2);
const FlexSpecification FlexLayoutTest::kUnboundedSnapToZero =
    FlexSpecification(MinimumFlexSizeRule::kPreferredSnapToZero,
                      MaximumFlexSizeRule::kUnbounded)
        .WithOrder(2);
const FlexSpecification FlexLayoutTest::kUnboundedScaleToMinimumSnapToZero =
    FlexSpecification(MinimumFlexSizeRule::kScaleToMinimumSnapToZero,
                      MaximumFlexSizeRule::kUnbounded)
        .WithOrder(2);
const FlexSpecification FlexLayoutTest::kUnboundedScaleToZero =
    FlexSpecification(MinimumFlexSizeRule::kScaleToZero,
                      MaximumFlexSizeRule::kUnbounded)
        .WithOrder(2);
const FlexSpecification FlexLayoutTest::kUnboundedScaleToZeroAdjustHeight =
    FlexSpecification(MinimumFlexSizeRule::kScaleToZero,
                      MaximumFlexSizeRule::kUnbounded,
                      true)
        .WithOrder(2);
const FlexSpecification FlexLayoutTest::kUnboundedScaleToMinimumHighPriority(
    MinimumFlexSizeRule::kScaleToMinimum,
    MaximumFlexSizeRule::kUnbounded);
const FlexSpecification FlexLayoutTest::kUnboundedScaleToMinimum =
    kUnboundedScaleToMinimumHighPriority.WithOrder(2);

const FlexSpecification FlexLayoutTest::kUnboundedSnapToMinimumHorizontal =
    FlexSpecification(LayoutOrientation::kHorizontal,
                      MinimumFlexSizeRule::kPreferredSnapToMinimum,
                      MaximumFlexSizeRule::kUnbounded)
        .WithOrder(2);
const FlexSpecification
    FlexLayoutTest::kUnboundedScaleToMinimumSnapToZeroHorizontal =
        FlexSpecification(LayoutOrientation::kHorizontal,
                          MinimumFlexSizeRule::kScaleToMinimumSnapToZero,
                          MaximumFlexSizeRule::kUnbounded)
            .WithOrder(2);
const FlexSpecification FlexLayoutTest::kUnboundedScaleToZeroHorizontal =
    FlexSpecification(LayoutOrientation::kHorizontal,
                      MinimumFlexSizeRule::kScaleToZero,
                      MaximumFlexSizeRule::kUnbounded)
        .WithOrder(2);

const FlexSpecification FlexLayoutTest::kScaleToMaximum =
    FlexSpecification(MinimumFlexSizeRule::kPreferred,
                      MaximumFlexSizeRule::kScaleToMaximum)
        .WithOrder(2);

const FlexSpecification FlexLayoutTest::kCustomFlex =
    FlexSpecification(base::BindRepeating(&CustomFlexImpl, false)).WithOrder(2);
const FlexSpecification FlexLayoutTest::kCustomFlexSnapToZero =
    FlexSpecification(base::BindRepeating(&CustomFlexImpl, true)).WithOrder(2);

}  // namespace

// Size Tests ------------------------------------------------------------------

TEST_F(FlexLayoutTest, GetMinimumSize_Empty) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(false);
  EXPECT_EQ(Size(0, 0), host_->GetMinimumSize());
}

TEST_F(FlexLayoutTest, GetMinimumSize_Empty_ViewInsets_Horizontal) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(false);
  host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
  EXPECT_EQ(Size(15, 12), host_->GetMinimumSize());
}

TEST_F(FlexLayoutTest, GetMinimumSize_Empty_ViewInsets_Vertical) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(false);
  host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
  EXPECT_EQ(Size(15, 12), host_->GetMinimumSize());
}

TEST_F(FlexLayoutTest, GetMinimumSize_Empty_InternalMargin_Collapsed) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  EXPECT_EQ(Size(9, 7), host_->GetMinimumSize());
}

TEST_F(FlexLayoutTest, GetMinimumSize_Empty_InternalMargin_NotCollapsed) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(false);
  layout_->SetInteriorMargin(kLayoutInsets);
  EXPECT_EQ(Size(15, 12), host_->GetMinimumSize());
}

TEST_F(FlexLayoutTest,
       GetMinimumSize_Empty_InternalMargin_DefaultMarginHasNoEffect) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(false);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(11));
  EXPECT_EQ(Size(15, 12), host_->GetMinimumSize());
}

TEST_F(FlexLayoutTest, GetMinimumSize_MinimumCross_Horizontal) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetMinimumCrossAxisSize(5);
  EXPECT_EQ(Size(9, 7), host_->GetMinimumSize());
  layout_->SetMinimumCrossAxisSize(10);
  EXPECT_EQ(Size(9, 10), host_->GetMinimumSize());
  host_->SetBorder(CreateEmptyBorder(kSmallInsets));
  EXPECT_EQ(Size(15, 14), host_->GetMinimumSize());
}

TEST_F(FlexLayoutTest, GetMinimumSize_MinimumCross_Vertical) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetMinimumCrossAxisSize(5);
  EXPECT_EQ(Size(9, 7), host_->GetMinimumSize());
  layout_->SetMinimumCrossAxisSize(10);
  EXPECT_EQ(Size(10, 7), host_->GetMinimumSize());
  host_->SetBorder(CreateEmptyBorder(kSmallInsets));
  EXPECT_EQ(Size(16, 11), host_->GetMinimumSize());
}

// Visibility and Inclusion Tests ----------------------------------------------

TEST_F(FlexLayoutTest, Layout_VisibilitySetBeforeInstall) {
  // Since our test fixture creates a host and adds the layout manager right
  // away, we need to create our own for this test.
  std::unique_ptr<views::View> host = std::make_unique<views::View>();
  View* child1 =
      AddChild(host.get(), Size(10, 10), std::optional<Size>(), false);
  View* child2 =
      AddChild(host.get(), Size(10, 10), std::optional<Size>(), true);
  host->SetLayoutManager(std::make_unique<FlexLayout>());

  test::RunScheduledLayout(host.get());
  EXPECT_FALSE(child1->GetVisible());
  EXPECT_TRUE(child2->GetVisible());

  child1->SetVisible(true);
  child2->SetVisible(false);

  test::RunScheduledLayout(host.get());
  EXPECT_TRUE(child1->GetVisible());
  EXPECT_FALSE(child2->GetVisible());
}

TEST_F(FlexLayoutTest, Layout_VisibilitySetAfterInstall) {
  // Unlike the last test, we'll use the built-in host and layout manager since
  // they're already set up.
  View* child1 = AddChild(Size(10, 10), std::optional<Size>(), false);
  View* child2 = AddChild(Size(10, 10), std::optional<Size>(), true);

  test::RunScheduledLayout(host_.get());
  EXPECT_FALSE(child1->GetVisible());
  EXPECT_TRUE(child2->GetVisible());

  child1->SetVisible(true);
  child2->SetVisible(false);

  test::RunScheduledLayout(host_.get());
  EXPECT_TRUE(child1->GetVisible());
  EXPECT_FALSE(child2->GetVisible());
}

TEST_F(FlexLayoutTest, Layout_VisibilitySetBeforeAdd) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size, optional<Size>(), false);
  View* child3 = AddChild(kChild3Size);

  test::RunScheduledLayout(host_.get());
  EXPECT_FALSE(child2->GetVisible());
  EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds());
  EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds());
  EXPECT_EQ(Size(44, 25), host_->GetPreferredSize({}));

  // This should have no additional effect since the child is already invisible.
  child2->SetVisible(false);
  test::RunScheduledLayout(host_.get());
  EXPECT_FALSE(child2->GetVisible());
  EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds());
  EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds());
  EXPECT_EQ(Size(44, 25), host_->GetPreferredSize({}));

  child2->SetVisible(true);
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(6, 5, 12, 10), Rect(18, 5, 13, 11),
                                Rect(31, 5, 17, 13)};
  EXPECT_TRUE(child2->GetVisible());
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(57, 25), host_->GetPreferredSize({}));
}

TEST_F(FlexLayoutTest, Layout_VisibilitySetAfterAdd) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size);
  View* child3 = AddChild(kChild3Size);

  child2->SetVisible(false);
  test::RunScheduledLayout(host_.get());
  EXPECT_FALSE(child2->GetVisible());
  EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds());
  EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds());
  EXPECT_EQ(Size(44, 25), host_->GetPreferredSize({}));

  child2->SetVisible(true);
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(6, 5, 12, 10), Rect(18, 5, 13, 11),
                                Rect(31, 5, 17, 13)};
  EXPECT_TRUE(child2->GetVisible());
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(57, 25), host_->GetPreferredSize({}));
}

TEST_F(FlexLayoutTest,
       Layout_ViewVisibilitySetNotContingentOnActualVisibility) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size);
  View* child3 = AddChild(kChild3Size);
  child2->SetProperty(views::kFlexBehaviorKey, kDropOut);

  // Layout makes child view invisible due to flex rule.
  host_->SetSize(Size(40, 25));
  test::RunScheduledLayout(host_.get());
  EXPECT_FALSE(child2->GetVisible());
  EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds());
  EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds());
  // Preferred size should still reflect child hidden due to flex rule.
  EXPECT_EQ(Size(57, 25), host_->GetPreferredSize({}));

  // Now we will make child explicitly hidden.
  child2->SetVisible(false);
  test::RunScheduledLayout(host_.get());
  EXPECT_FALSE(child2->GetVisible());
  EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds());
  EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds());
  EXPECT_EQ(Size(44, 25), host_->GetPreferredSize({}));
}

TEST_F(FlexLayoutTest, Layout_Exlcude) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  const View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size);
  const View* child3 = AddChild(kChild3Size);

  child2->SetProperty(kViewIgnoredByLayoutKey, true);
  child2->SetBounds(3, 3, 3, 3);
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Rect(3, 3, 3, 3), child2->bounds());
  EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds());
  EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds());
  EXPECT_EQ(Size(44, 25), host_->GetPreferredSize({}));

  child2->SetProperty(kViewIgnoredByLayoutKey, false);
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(6, 5, 12, 10), Rect(18, 5, 13, 11),
                                Rect(31, 5, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(57, 25), host_->GetPreferredSize({}));
}

// Child Positioning Tests -----------------------------------------------------

TEST_F(FlexLayoutTest, LayoutSingleView_Horizontal) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  View* child = AddChild(kChild1Size);
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Rect(6, 5, 12, 10), child->bounds());
}

TEST_F(FlexLayoutTest, LayoutSingleView_Vertical) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  View* child = AddChild(kChild1Size);
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Rect(6, 5, 12, 10), child->bounds());
}

TEST_F(FlexLayoutTest, LayoutMultipleViews_Horizontal_CrossStart) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  AddChild(kChild1Size);
  AddChild(kChild2Size);
  AddChild(kChild3Size);
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(6, 5, 12, 10), Rect(18, 5, 13, 11),
                                Rect(31, 5, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(57, 25), host_->GetPreferredSize({}));
}

TEST_F(FlexLayoutTest, LayoutMultipleViews_Horizontal_CrossCenter) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kCenter);
  AddChild(kChild1Size);
  AddChild(kChild2Size);
  AddChild(kChild3Size);
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(6, 6, 12, 10), Rect(18, 6, 13, 11),
                                Rect(31, 5, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(57, 25), host_->GetPreferredSize({}));
}

TEST_F(FlexLayoutTest, LayoutMultipleViews_Horizontal_CrossEnd) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kEnd);
  AddChild(kChild1Size);
  AddChild(kChild2Size);
  AddChild(kChild3Size);
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(6, 8, 12, 10), Rect(18, 7, 13, 11),
                                Rect(31, 5, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(57, 25), host_->GetPreferredSize({}));
}

TEST_F(FlexLayoutTest, LayoutMultipleViews_Horizontal_CrossStretch) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
  host_->SetSize(Size(100, 25));
  AddChild(kChild1Size);
  AddChild(kChild2Size);
  AddChild(kChild3Size);
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(6, 5, 12, 13), Rect(18, 5, 13, 13),
                                Rect(31, 5, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(57, 25), host_->GetPreferredSize({}));
}

TEST_F(FlexLayoutTest, LayoutMultipleViews_Vertical_CrossStart) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  AddChild(kChild1Size);
  AddChild(kChild2Size);
  AddChild(kChild3Size);
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(6, 5, 12, 10), Rect(6, 15, 13, 11),
                                Rect(6, 26, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(32, 46), host_->GetPreferredSize({}));
}

TEST_F(FlexLayoutTest, LayoutMultipleViews_Vertical_CrossCenter) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kCenter);
  AddChild(kChild1Size);
  AddChild(kChild2Size);
  AddChild(kChild3Size);
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(8, 5, 12, 10), Rect(8, 15, 13, 11),
                                Rect(6, 26, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(32, 46), host_->GetPreferredSize({}));
}

TEST_F(FlexLayoutTest, LayoutMultipleViews_Vertical_CrossEnd) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kEnd);
  AddChild(kChild1Size);
  AddChild(kChild2Size);
  AddChild(kChild3Size);
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(11, 5, 12, 10), Rect(10, 15, 13, 11),
                                Rect(6, 26, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(32, 46), host_->GetPreferredSize({}));
}

TEST_F(FlexLayoutTest, LayoutMultipleViews_Vertical_CrossStretch) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
  AddChild(kChild1Size);
  AddChild(kChild2Size);
  AddChild(kChild3Size);
  host_->SetSize(Size(32, 50));
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(6, 5, 17, 10), Rect(6, 15, 17, 11),
                                Rect(6, 26, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(32, 46), host_->GetPreferredSize({}));
}

TEST_F(FlexLayoutTest,
       LayoutMultipleViews_MarginAndSpacing_NoCollapse_Horizontal) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(false);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size);
  View* child3 = AddChild(kChild3Size);
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(6, 5, 12, 10), Rect(18, 5, 13, 11),
                                Rect(31, 5, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(57, 25), host_->GetPreferredSize({}));

  child1->SetProperty(views::kMarginsKey, Insets::TLBR(20, 21, 22, 23));
  host_->InvalidateLayout();
  test::RunScheduledLayout(host_.get());
  expected = std::vector<Rect>{Rect(27, 25, 12, 10), Rect(62, 5, 13, 11),
                               Rect(75, 5, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(101, 64), host_->GetPreferredSize({}));

  child2->SetProperty(views::kMarginsKey, Insets(1));
  host_->InvalidateLayout();
  layout_->SetDefault(views::kMarginsKey, gfx::Insets::VH(0, 3));
  test::RunScheduledLayout(host_.get());
  expected = std::vector<Rect>{Rect(27, 25, 12, 10), Rect(63, 6, 13, 11),
                               Rect(80, 5, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(109, 64), host_->GetPreferredSize({}));

  child3->SetProperty(views::kMarginsKey, Insets(2));
  host_->InvalidateLayout();
  test::RunScheduledLayout(host_.get());
  expected = std::vector<Rect>{Rect(27, 25, 12, 10), Rect(63, 6, 13, 11),
                               Rect(79, 7, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(107, 64), host_->GetPreferredSize({}));
}

TEST_F(FlexLayoutTest,
       LayoutMultipleViews_MarginAndSpacing_NoCollapse_Vertical) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(false);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
  View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size);
  View* child3 = AddChild(kChild3Size);
  child1->SetProperty(views::kMarginsKey, Insets::TLBR(20, 21, 22, 23));
  child2->SetProperty(views::kMarginsKey, Insets(1));
  child3->SetProperty(views::kMarginsKey, Insets(2));
  host_->InvalidateLayout();
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(27, 25, 12, 10), Rect(7, 58, 13, 11),
                                Rect(8, 72, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(71, 94), host_->GetPreferredSize({}));
}

TEST_F(FlexLayoutTest,
       LayoutMultipleViews_MarginAndSpacing_Collapse_Horizontal) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
  View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size);
  View* child3 = AddChild(kChild3Size);
  child1->SetProperty(views::kMarginsKey, Insets::TLBR(20, 21, 22, 23));
  child2->SetProperty(views::kMarginsKey, Insets(1));
  child3->SetProperty(views::kMarginsKey, Insets(2));
  host_->InvalidateLayout();
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(21, 20, 12, 10), Rect(56, 5, 13, 11),
                                Rect(71, 5, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(97, 52), host_->GetPreferredSize({}));
}

TEST_F(FlexLayoutTest, LayoutMultipleViews_MarginAndSpacing_Collapse_Vertical) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
  View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size);
  View* child3 = AddChild(kChild3Size);
  child1->SetProperty(views::kMarginsKey, Insets::TLBR(20, 21, 22, 23));
  child2->SetProperty(views::kMarginsKey, Insets(1));
  child3->SetProperty(views::kMarginsKey, Insets(2));
  host_->InvalidateLayout();
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(21, 20, 12, 10), Rect(6, 52, 13, 11),
                                Rect(6, 65, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(56, 85), host_->GetPreferredSize({}));
}

TEST_F(FlexLayoutTest, LayoutMultipleViews_InteriorPadding) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(10));
  View* child = AddChild(Size(13, 15));
  AddChild(kChild3Size);
  child->SetProperty(views::kInternalPaddingKey, Insets::TLBR(1, 2, 4, 8));
  host_->InvalidateLayout();
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {
      Rect(8, 9, 13, 15),
      Rect(23, 10, 17, 13),
  };
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(50, 33), host_->GetPreferredSize({}));
}

TEST_F(FlexLayoutTest, LayoutMultipleViews_InteriorPadding_Margins) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(2));
  View* child = AddChild(Size(13, 15));
  View* child2 = AddChild(kChild3Size);
  child->SetProperty(views::kInternalPaddingKey, Insets::TLBR(1, 2, 4, 8));
  child2->SetProperty(views::kMarginsKey, Insets(5));
  host_->InvalidateLayout();
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {
      Rect(4, 4, 13, 15),
      Rect(17, 5, 17, 13),
  };
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(43, 25), host_->GetPreferredSize({}));
}

TEST_F(FlexLayoutTest, LayoutMultipleViews_InteriorPadding_Additive) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(20));
  View* child = AddChild(Size(13, 15));
  View* child2 = AddChild(kChild3Size);
  child->SetProperty(views::kInternalPaddingKey, Insets::TLBR(1, 2, 4, 8));
  child2->SetProperty(views::kInternalPaddingKey, Insets(5));
  host_->InvalidateLayout();
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {
      Rect(18, 19, 13, 15),
      Rect(38, 15, 17, 13),
  };
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_EQ(Size(70, 50), host_->GetPreferredSize({}));
}

// Height-for-width tests ------------------------------------------------------

TEST_F(FlexLayoutTest, HeightForWidth_Vertical_CrossStart) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(kMarginsKey, gfx::Insets(5));
  layout_->SetDefault(kFlexBehaviorKey, kPreferredAdjustHeight);
  AddChild({10, 10})->set_size_mode(MockView::SizeMode::kFixedArea);
  AddChild({10, 10});

  EXPECT_EQ(gfx::Size(20, 40), host_->GetPreferredSize({}));
  EXPECT_EQ(40, host_->GetHeightForWidth(26));
  EXPECT_EQ(40, host_->GetHeightForWidth(20));
  EXPECT_EQ(46, host_->GetHeightForWidth(16));

  host_->SizeToPreferredSize();
  std::vector<gfx::Rect> expected = {{5, 5, 10, 10}, {5, 25, 10, 10}};
  EXPECT_EQ(expected, GetChildBounds());
  host_->SetSize({26, 50});
  EXPECT_EQ(expected, GetChildBounds());
  host_->SetSize({20, 50});
  EXPECT_EQ(expected, GetChildBounds());
  host_->SetSize({16, 50});
  expected = {{5, 5, 6, 16}, {5, 31, 6, 10}};
  EXPECT_EQ(expected, GetChildBounds());
}

TEST_F(FlexLayoutTest,
       HeightForWidth_Vertical_CrossStretch_WidthChangesHeight) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
  layout_->SetDefault(kMarginsKey, gfx::Insets(5));
  layout_->SetDefault(kFlexBehaviorKey, kPreferredAdjustHeight);
  AddChild({10, 10})->set_size_mode(MockView::SizeMode::kFixedArea);
  AddChild({10, 10});

  EXPECT_EQ(gfx::Size(20, 40), host_->GetPreferredSize({}));
  EXPECT_EQ(40, host_->GetHeightForWidth(26));
  EXPECT_EQ(40, host_->GetHeightForWidth(20));
  EXPECT_EQ(46, host_->GetHeightForWidth(16));

  host_->SizeToPreferredSize();
  std::vector<gfx::Rect> expected = {{5, 5, 10, 10}, {5, 25, 10, 10}};
  EXPECT_EQ(expected, GetChildBounds());

  host_->SetSize({26, 50});
  expected = {{5, 5, 16, 10}, {5, 25, 16, 10}};
  EXPECT_EQ(expected, GetChildBounds());

  host_->SetSize({20, 50});
  expected = {{5, 5, 10, 10}, {5, 25, 10, 10}};
  EXPECT_EQ(expected, GetChildBounds());

  host_->SetSize({16, 50});
  expected = {{5, 5, 6, 16}, {5, 31, 6, 10}};
  EXPECT_EQ(expected, GetChildBounds());
}

TEST_F(FlexLayoutTest, HeightForWidth_Vertical_CrossStretch_FlexPreferredSize) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
  layout_->SetDefault(kMarginsKey, gfx::Insets(5));
  layout_->SetDefault(kFlexBehaviorKey, kUnboundedScaleToZeroAdjustHeight);
  AddChild({10, 10})->set_size_mode(MockView::SizeMode::kFixedArea);
  AddChild({10, 10});

  EXPECT_EQ(gfx::Size(20, 40), host_->GetPreferredSize({}));
  EXPECT_EQ(40, host_->GetHeightForWidth(26));
  EXPECT_EQ(40, host_->GetHeightForWidth(20));
  EXPECT_EQ(46, host_->GetHeightForWidth(16));

  host_->SizeToPreferredSize();
  test::RunScheduledLayout(host_.get());
  std::vector<gfx::Rect> expected = {{5, 5, 10, 10}, {5, 25, 10, 10}};
  EXPECT_EQ(expected, GetChildBounds());
}

TEST_F(FlexLayoutTest, HeightForWidth_Vertical_CrossStretch_FlexLarger) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
  layout_->SetDefault(kMarginsKey, gfx::Insets(5));
  layout_->SetDefault(kFlexBehaviorKey, kUnboundedScaleToZeroAdjustHeight);
  AddChild({10, 10})->set_size_mode(MockView::SizeMode::kFixedArea);
  AddChild({10, 10});

  host_->SetSize({26, 50});
  test::RunScheduledLayout(host_.get());
  std::vector<gfx::Rect> expected = {{5, 5, 16, 15}, {5, 30, 16, 15}};
  EXPECT_EQ(expected, GetChildBounds());

  host_->SetSize({20, 50});
  test::RunScheduledLayout(host_.get());
  expected = {{5, 5, 10, 15}, {5, 30, 10, 15}};
  EXPECT_EQ(expected, GetChildBounds());

  host_->SetSize({16, 50});
  test::RunScheduledLayout(host_.get());
  expected = {{5, 5, 6, 18}, {5, 33, 6, 12}};
  EXPECT_EQ(expected, GetChildBounds());
}

TEST_F(FlexLayoutTest, HeightForWidth_Vertical_CrossStretch_FlexSmaller) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
  layout_->SetDefault(kMarginsKey, gfx::Insets(5));
  layout_->SetDefault(kFlexBehaviorKey, kUnboundedScaleToZeroAdjustHeight);
  AddChild({10, 10})->set_size_mode(MockView::SizeMode::kFixedArea);
  AddChild({10, 10});

  EXPECT_EQ(gfx::Size(20, 40), host_->GetPreferredSize({}));
  EXPECT_EQ(40, host_->GetHeightForWidth(26));
  EXPECT_EQ(40, host_->GetHeightForWidth(20));
  EXPECT_EQ(46, host_->GetHeightForWidth(16));

  host_->SetSize({26, 30});
  test::RunScheduledLayout(host_.get());
  std::vector<gfx::Rect> expected = {{5, 5, 16, 5}, {5, 20, 16, 5}};
  EXPECT_EQ(expected, GetChildBounds());

  host_->SetSize({20, 30});
  test::RunScheduledLayout(host_.get());
  expected = {{5, 5, 10, 5}, {5, 20, 10, 5}};
  EXPECT_EQ(expected, GetChildBounds());

  host_->SetSize({16, 30});
  test::RunScheduledLayout(host_.get());
  expected = {{5, 5, 6, 8}, {5, 23, 6, 2}};
  EXPECT_EQ(expected, GetChildBounds());
}

TEST_F(FlexLayoutTest, HeightForWidth_Horizontal_PreferredSize) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(kFlexBehaviorKey, kUnboundedScaleToZeroAdjustHeight);
  MockView* const child = AddChild({10, 10});
  child->set_size_mode(MockView::SizeMode::kFixedArea);

  // In horizontal views, the height can expand if a child is compressed
  // horizontally and uses a height-for-width calculation, but it cannot
  // contract (lest we have zero-height views in some cases).
  EXPECT_EQ(gfx::Size(10, 10), host_->GetPreferredSize({}));
  EXPECT_EQ(10, host_->GetHeightForWidth(10));
  EXPECT_EQ(10, host_->GetHeightForWidth(20));
  EXPECT_EQ(20, host_->GetHeightForWidth(5));
}

// Host insets tests -----------------------------------------------------------

TEST_F(FlexLayoutTest, Layout_HostInsets_Horizontal) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
  View* child = AddChild(kChild1Size);
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Rect(6, 5, 12, 10), child->bounds());
}

TEST_F(FlexLayoutTest, Layout_HostInsets_Vertical) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
  View* child = AddChild(kChild1Size);
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Rect(6, 5, 12, 10), child->bounds());
}

TEST_F(FlexLayoutTest, Layout_HostInsets_Horizontal_Leading) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
  View* child = AddChild(kChild1Size);
  host_->SetSize({100, 100});
  EXPECT_EQ(Rect(6, 5, 12, 10), child->bounds());
}

TEST_F(FlexLayoutTest, Layout_HostInsets_Vertical_Leading) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
  View* child = AddChild(kChild1Size);
  host_->SetSize({100, 100});
  EXPECT_EQ(Rect(6, 5, 12, 10), child->bounds());
}

TEST_F(FlexLayoutTest, Layout_HostInsets_Horizontal_Center) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetMainAxisAlignment(LayoutAlignment::kCenter);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
  View* child = AddChild(kChild1Size);
  host_->SetSize({100, 100});
  const int expected_x =
      kLayoutInsets.left() +
      (host_->size().width() - kChild1Size.width() - kLayoutInsets.width()) / 2;
  EXPECT_EQ(Rect(expected_x, 5, 12, 10), child->bounds());
}

TEST_F(FlexLayoutTest, Layout_HostInsets_Vertical_Center) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetMainAxisAlignment(LayoutAlignment::kCenter);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
  View* child = AddChild(kChild1Size);
  host_->SetSize({100, 100});
  const int expected_y =
      kLayoutInsets.top() +
      (host_->size().height() - kChild1Size.height() - kLayoutInsets.height()) /
          2;
  EXPECT_EQ(Rect(6, expected_y, 12, 10), child->bounds());
}

TEST_F(FlexLayoutTest, Layout_HostInsets_Horizontal_End) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetMainAxisAlignment(LayoutAlignment::kEnd);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
  View* child = AddChild(kChild1Size);
  host_->SetSize({100, 100});
  const int expected_x =
      kLayoutInsets.left() +
      (host_->size().width() - kChild1Size.width() - kLayoutInsets.width());
  EXPECT_EQ(Rect(expected_x, 5, 12, 10), child->bounds());
}

TEST_F(FlexLayoutTest, Layout_HostInsets_Vertical_End) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetMainAxisAlignment(LayoutAlignment::kEnd);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  host_->SetBorder(CreateEmptyBorder(kLayoutInsets));
  View* child = AddChild(kChild1Size);
  host_->SetSize({100, 100});
  const int expected_y =
      kLayoutInsets.top() +
      (host_->size().height() - kChild1Size.height() - kLayoutInsets.height());
  EXPECT_EQ(Rect(6, expected_y, 12, 10), child->bounds());
}

// Include Host Insets Tests ---------------------------------------------------

TEST_F(FlexLayoutTest, SetIncludeHostInsetsInLayout_NoChange) {
  host_->SetBorder(views::CreateEmptyBorder(2));
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(false);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kEnd);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(4));
  constexpr Size kChildSize(10, 10);
  AddChild(kChildSize);
  View* const child2 = AddChild(kChildSize);
  AddChild(kChildSize);
  child2->SetProperty(views::kMarginsKey, gfx::Insets(10));

  const Size expected_preferred_size = host_->GetPreferredSize({});
  host_->SetSize(expected_preferred_size);
  const std::vector<Rect> expected_bounds = GetChildBounds();

  layout_->SetIncludeHostInsetsInLayout(true);
  const Size preferred_size = host_->GetPreferredSize({});
  EXPECT_EQ(expected_preferred_size, preferred_size);
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(expected_bounds, GetChildBounds());
}

TEST_F(FlexLayoutTest, SetIncludeHostInsetsInLayout_CollapseIntoInsets) {
  host_->SetBorder(views::CreateEmptyBorder(Insets(2)));
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kEnd);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(4));
  constexpr Size kChildSize(10, 10);
  AddChild(kChildSize);
  View* const child2 = AddChild(kChildSize);
  AddChild(kChildSize);
  child2->SetProperty(views::kMarginsKey, gfx::Insets(15));

  layout_->SetIncludeHostInsetsInLayout(true);
  const Size preferred_size = host_->GetPreferredSize({});
  EXPECT_EQ(Size(40, 76), preferred_size);
  host_->SetSize(preferred_size);
  const std::vector<Rect> expected = {Rect(19, 7, 10, 10), Rect(15, 32, 10, 10),
                                      Rect(19, 57, 10, 10)};
  EXPECT_EQ(expected, GetChildBounds());
}

TEST_F(FlexLayoutTest, SetIncludeHostInsetsInLayout_OverlapInsets) {
  host_->SetBorder(views::CreateEmptyBorder(gfx::Insets::TLBR(4, 5, 5, 5)));
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  View* const child = AddChild(Size(10, 10));
  child->SetProperty(views::kInternalPaddingKey, Insets(10));

  layout_->SetIncludeHostInsetsInLayout(true);
  const Size preferred_size = host_->GetPreferredSize({});
  EXPECT_EQ(Size(15, 12), preferred_size);
  host_->SetSize(preferred_size);
  EXPECT_EQ(Rect(1, 0, 10, 10), child->bounds());
}

// Default Main Axis Margins Tests ---------------------------------------------

TEST_F(FlexLayoutTest, SetIgnoreDefaultMainAxisMargins_IgnoresDefaultMargins) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(false);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kEnd);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(4));
  constexpr Size kChildSize(10, 10);
  AddChild(kChildSize);
  AddChild(kChildSize);
  AddChild(kChildSize);

  Size preferred_size = host_->GetPreferredSize({});
  EXPECT_EQ(Size(33, 66), preferred_size);

  layout_->SetIgnoreDefaultMainAxisMargins(true);
  preferred_size = host_->GetPreferredSize({});
  EXPECT_EQ(Size(33, 58), preferred_size);

  host_->SetSize(preferred_size);
  const std::vector<Rect> expected = {Rect(10, 5, 10, 10), Rect(10, 23, 10, 10),
                                      Rect(10, 41, 10, 10)};
  EXPECT_EQ(expected, GetChildBounds());
}

TEST_F(FlexLayoutTest,
       SetIgnoreDefaultMainAxisMargins_IncludesExplicitMargins) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(4));
  constexpr Size kChildSize(10, 10);
  View* const child1 = AddChild(kChildSize);
  AddChild(kChildSize);
  View* const child3 = AddChild(kChildSize);

  child1->SetProperty(views::kMarginsKey, gfx::Insets(11));
  child3->SetProperty(views::kMarginsKey, gfx::Insets(12));

  Size preferred_size = host_->GetPreferredSize({});
  EXPECT_EQ(Size(34, 76), preferred_size);

  layout_->SetIgnoreDefaultMainAxisMargins(true);
  preferred_size = host_->GetPreferredSize({});
  EXPECT_EQ(Size(34, 76), preferred_size);

  host_->SetSize(preferred_size);
  const std::vector<Rect> expected = {Rect(11, 11, 10, 10), Rect(6, 32, 10, 10),
                                      Rect(12, 54, 10, 10)};
  EXPECT_EQ(expected, GetChildBounds());
}

// Alignment Tests -------------------------------------------------------------

TEST_F(FlexLayoutTest, Layout_CrossStart) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size);
  View* child3 = AddChild(kChild3Size);
  child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets));
  child2->SetProperty(views::kMarginsKey, Insets(1));
  child3->SetProperty(views::kMarginsKey, Insets(2));
  host_->SetSize(Size(200, 200));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(10, child1->origin().y());
  EXPECT_EQ(5, child2->origin().y());
  EXPECT_EQ(5, child3->origin().y());
}

TEST_F(FlexLayoutTest, Layout_CrossCenter) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kCenter);
  View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size);
  View* child3 = AddChild(kChild3Size);
  child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets));
  child2->SetProperty(views::kMarginsKey, Insets(1));
  child3->SetProperty(views::kMarginsKey, Insets(2));
  host_->SetSize(Size(200, 200));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(94, child1->origin().y());
  EXPECT_EQ(93, child2->origin().y());
  EXPECT_EQ(92, child3->origin().y());
}

TEST_F(FlexLayoutTest, Layout_CrossEnd) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kEnd);
  View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size);
  View* child3 = AddChild(kChild3Size);
  child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets));
  child2->SetProperty(views::kMarginsKey, Insets(1));
  child3->SetProperty(views::kMarginsKey, Insets(2));
  host_->SetSize(Size(200, 200));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(178, child1->origin().y());
  EXPECT_EQ(182, child2->origin().y());
  EXPECT_EQ(180, child3->origin().y());
}

TEST_F(FlexLayoutTest, Layout_CrossStretch) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
  View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size);
  View* child3 = AddChild(kChild3Size);
  child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets));
  child2->SetProperty(views::kMarginsKey, Insets(1));
  child3->SetProperty(views::kMarginsKey, Insets(2));
  host_->SetSize(Size(200, 200));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(10, child1->origin().y());
  EXPECT_EQ(5, child2->origin().y());
  EXPECT_EQ(5, child3->origin().y());
  EXPECT_EQ(178, child1->size().height());
  EXPECT_EQ(188, child2->size().height());
  EXPECT_EQ(188, child3->size().height());
}

TEST_F(FlexLayoutTest, Layout_AlignStart) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
  View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size);
  View* child3 = AddChild(kChild3Size);
  child1->SetProperty(views::kMarginsKey, Insets::TLBR(20, 21, 22, 23));
  child2->SetProperty(views::kMarginsKey, Insets(1));
  child3->SetProperty(views::kMarginsKey, Insets(2));
  host_->SetSize(Size(105, 50));
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(21, 20, 12, 10), Rect(56, 5, 13, 11),
                                Rect(71, 5, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
}

TEST_F(FlexLayoutTest, Layout_AlignCenter) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetMainAxisAlignment(LayoutAlignment::kCenter);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
  View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size);
  View* child3 = AddChild(kChild3Size);
  child1->SetProperty(views::kMarginsKey, Insets::TLBR(20, 21, 22, 23));
  child2->SetProperty(views::kMarginsKey, Insets(1));
  child3->SetProperty(views::kMarginsKey, Insets(2));
  host_->SetSize(Size(105, 50));
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(25, 20, 12, 10), Rect(60, 5, 13, 11),
                                Rect(75, 5, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
}

TEST_F(FlexLayoutTest, Layout_AlignEnd) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetMainAxisAlignment(LayoutAlignment::kEnd);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
  View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size);
  View* child3 = AddChild(kChild3Size);
  child1->SetProperty(views::kMarginsKey, Insets::TLBR(20, 21, 22, 23));
  child2->SetProperty(views::kMarginsKey, Insets(1));
  child3->SetProperty(views::kMarginsKey, Insets(2));
  host_->SetSize(Size(105, 50));
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(29, 20, 12, 10), Rect(64, 5, 13, 11),
                                Rect(79, 5, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
}

TEST_F(FlexLayoutTest, Layout_AddDroppedMargins) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(false);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  View* child1 = AddChild(Size(10, 10));
  View* child2 = AddChild(Size(10, 10));
  View* child3 = AddChild(Size(10, 10));
  child2->SetProperty(views::kMarginsKey, Insets(1));
  child2->SetProperty(views::kFlexBehaviorKey, kDropOut);
  EXPECT_EQ(Size(30, 20), host_->GetMinimumSize());

  host_->SetSize(Size(100, 50));
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(5, 5, 10, 10), Rect(16, 6, 10, 10),
                                Rect(27, 5, 10, 10)};
  EXPECT_EQ(expected, GetChildBounds());

  host_->SetSize(Size(25, 50));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Rect(5, 5, 10, 10), child1->bounds());
  EXPECT_FALSE(child2->GetVisible());
  EXPECT_EQ(Rect(15, 5, 10, 10), child3->bounds());
}

TEST_F(FlexLayoutTest, Layout_VerticalAlign_WiderThanTall) {
  // This test ensures we do not regress http://crbug.com/983941
  // Previously, the width of the host view was erroneously used when
  // calculating excess main-axis size, causing center-alignment in vertical
  // layouts in host views that were much wider than tall to be incorrect.
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetMainAxisAlignment(LayoutAlignment::kCenter);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
  View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size);
  View* child3 = AddChild(kChild3Size);
  child1->SetProperty(views::kMarginsKey, Insets::TLBR(20, 21, 22, 23));
  child2->SetProperty(views::kMarginsKey, Insets(1));
  child3->SetProperty(views::kMarginsKey, Insets(2));
  host_->SetSize(Size(1000, 100));
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(21, 27, 12, 10), Rect(6, 59, 13, 11),
                                Rect(6, 72, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());
}

// Flex Tests ------------------------------------------------------------------

TEST_F(FlexLayoutTest, Layout_IgnoreMinimumSize_DropViews) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
  View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size);
  View* child3 = AddChild(kChild3Size);
  child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets));
  child2->SetProperty(views::kMarginsKey, Insets(1));
  child3->SetProperty(views::kMarginsKey, Insets(2));
  host_->SetSize(Size(55, 50));
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected = {Rect(11, 10, 12, 10), Rect(36, 5, 13, 11),
                                Rect(51, 5, 17, 13)};
  EXPECT_EQ(expected, GetChildBounds());

  child1->SetProperty(views::kFlexBehaviorKey, kDropOut);
  host_->InvalidateLayout();
  EXPECT_EQ(Size(77, 32), host_->GetPreferredSize({}));
  EXPECT_EQ(Size(47, 25), host_->GetMinimumSize());
  test::RunScheduledLayout(host_.get());
  EXPECT_FALSE(child1->GetVisible());
  EXPECT_TRUE(child2->GetVisible());
  EXPECT_TRUE(child3->GetVisible());
  EXPECT_EQ(Rect(6, 5, 13, 11), child2->bounds());
  EXPECT_EQ(Rect(21, 5, 17, 13), child3->bounds());

  child1->ClearProperty(views::kFlexBehaviorKey);
  child2->SetProperty(views::kFlexBehaviorKey, kDropOut);
  host_->InvalidateLayout();
  EXPECT_EQ(Size(77, 32), host_->GetPreferredSize({}));
  EXPECT_EQ(Size(62, 32), host_->GetMinimumSize());
  test::RunScheduledLayout(host_.get());
  EXPECT_TRUE(child1->GetVisible());
  EXPECT_FALSE(child2->GetVisible());
  EXPECT_TRUE(child3->GetVisible());
  EXPECT_EQ(Rect(11, 10, 12, 10), child1->bounds());
  EXPECT_EQ(Rect(36, 5, 17, 13), child3->bounds());

  child2->ClearProperty(views::kFlexBehaviorKey);
  child3->SetProperty(views::kFlexBehaviorKey, kDropOut);
  host_->InvalidateLayout();
  EXPECT_EQ(Size(77, 32), host_->GetPreferredSize({}));
  EXPECT_EQ(Size(58, 32), host_->GetMinimumSize());
  test::RunScheduledLayout(host_.get());
  EXPECT_TRUE(child1->GetVisible());
  EXPECT_TRUE(child2->GetVisible());
  EXPECT_FALSE(child3->GetVisible());
  EXPECT_EQ(Rect(11, 10, 12, 10), child1->bounds());
  EXPECT_EQ(Rect(36, 5, 13, 11), child2->bounds());
}

TEST_F(FlexLayoutTest, Layout_IgnoreMinimumSize_DropInOrder) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
  View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size);
  View* child3 = AddChild(kChild3Size);
  child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets));
  child2->SetProperty(views::kMarginsKey, Insets(1));
  child3->SetProperty(views::kMarginsKey, Insets(2));
  // Set flex separately; we'll test default flex later.
  child1->SetProperty(views::kFlexBehaviorKey, kDropOut);
  child2->SetProperty(views::kFlexBehaviorKey, kDropOut);
  child3->SetProperty(views::kFlexBehaviorKey, kDropOut);
  EXPECT_EQ(Size(9, 7), host_->GetMinimumSize());

  host_->SetSize(Size(100, 50));
  test::RunScheduledLayout(host_.get());
  EXPECT_TRUE(child1->GetVisible());
  EXPECT_TRUE(child2->GetVisible());
  EXPECT_TRUE(child3->GetVisible());

  host_->SetSize(Size(58, 50));
  test::RunScheduledLayout(host_.get());
  EXPECT_TRUE(child1->GetVisible());
  EXPECT_TRUE(child2->GetVisible());
  EXPECT_FALSE(child3->GetVisible());

  host_->SetSize(Size(57, 50));
  test::RunScheduledLayout(host_.get());
  EXPECT_TRUE(child1->GetVisible());
  EXPECT_FALSE(child2->GetVisible());
  EXPECT_FALSE(child3->GetVisible());

  // Since there's no room for child1, child2 becomes visible.
  host_->SetSize(Size(28, 50));
  test::RunScheduledLayout(host_.get());
  EXPECT_FALSE(child1->GetVisible());
  EXPECT_TRUE(child2->GetVisible());
  EXPECT_FALSE(child3->GetVisible());

  host_->SetSize(Size(27, 50));
  test::RunScheduledLayout(host_.get());
  EXPECT_FALSE(child1->GetVisible());
  EXPECT_FALSE(child2->GetVisible());
  EXPECT_FALSE(child3->GetVisible());
}

TEST_F(FlexLayoutTest, Layout_IgnoreMinimumSize_DropInOrder_DefaultFlex) {
  // Perform the same test as above but with default flex set instead.
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
  View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size);
  View* child3 = AddChild(kChild3Size);
  child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets));
  child2->SetProperty(views::kMarginsKey, Insets(1));
  child3->SetProperty(views::kMarginsKey, Insets(2));
  layout_->SetDefault(views::kFlexBehaviorKey, kDropOut);
  EXPECT_EQ(Size(9, 7), host_->GetMinimumSize());

  host_->SetSize(Size(100, 50));
  test::RunScheduledLayout(host_.get());
  EXPECT_TRUE(child1->GetVisible());
  EXPECT_TRUE(child2->GetVisible());
  EXPECT_TRUE(child3->GetVisible());

  host_->SetSize(Size(58, 50));
  test::RunScheduledLayout(host_.get());
  EXPECT_TRUE(child1->GetVisible());
  EXPECT_TRUE(child2->GetVisible());
  EXPECT_FALSE(child3->GetVisible());

  host_->SetSize(Size(57, 50));
  test::RunScheduledLayout(host_.get());
  EXPECT_TRUE(child1->GetVisible());
  EXPECT_FALSE(child2->GetVisible());
  EXPECT_FALSE(child3->GetVisible());

  // Since there's no room for child1, child2 becomes visible.
  host_->SetSize(Size(28, 50));
  test::RunScheduledLayout(host_.get());
  EXPECT_FALSE(child1->GetVisible());
  EXPECT_TRUE(child2->GetVisible());
  EXPECT_FALSE(child3->GetVisible());

  host_->SetSize(Size(27, 50));
  test::RunScheduledLayout(host_.get());
  EXPECT_FALSE(child1->GetVisible());
  EXPECT_FALSE(child2->GetVisible());
  EXPECT_FALSE(child3->GetVisible());
}

TEST_F(FlexLayoutTest, Layout_IgnoreMinimumSize_DropByPriority) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(kLayoutInsets);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(3));
  View* child1 = AddChild(kChild1Size);
  View* child2 = AddChild(kChild2Size);
  View* child3 = AddChild(kChild3Size);
  child1->SetProperty(views::kMarginsKey, Insets(kLargeInsets));
  child2->SetProperty(views::kMarginsKey, Insets(1));
  child3->SetProperty(views::kMarginsKey, Insets(2));
  layout_->SetDefault(views::kFlexBehaviorKey, kDropOut);
  child3->SetProperty(views::kFlexBehaviorKey, kDropOutHighPriority);
  EXPECT_EQ(Size(9, 7), host_->GetMinimumSize());

  host_->SetSize(Size(100, 50));
  test::RunScheduledLayout(host_.get());
  EXPECT_TRUE(child1->GetVisible());
  EXPECT_TRUE(child2->GetVisible());
  EXPECT_TRUE(child3->GetVisible());

  host_->SetSize(Size(65, 50));
  test::RunScheduledLayout(host_.get());
  EXPECT_TRUE(child1->GetVisible());
  EXPECT_FALSE(child2->GetVisible());
  EXPECT_TRUE(child3->GetVisible());

  host_->SetSize(Size(40, 50));
  test::RunScheduledLayout(host_.get());
  EXPECT_FALSE(child1->GetVisible());
  EXPECT_FALSE(child2->GetVisible());
  EXPECT_TRUE(child3->GetVisible());

  host_->SetSize(Size(20, 50));
  test::RunScheduledLayout(host_.get());
  EXPECT_FALSE(child1->GetVisible());
  EXPECT_FALSE(child2->GetVisible());
  EXPECT_FALSE(child3->GetVisible());
}

TEST_F(FlexLayoutTest, Layout_Flex_OneViewScales) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child1 = AddChild(Size(10, 20), Size(5, 5));
  View* child2 = AddChild(Size(10, 10));
  child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);

  host_->SetSize(Size(20, 50));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(10, 20), child1->size());
  EXPECT_EQ(Size(10, 10), child2->size());

  host_->SetSize(Size(20, 35));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(10, 10), child1->size());
  EXPECT_EQ(Size(10, 10), child2->size());

  host_->SetSize(Size(20, 30));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(10, 5), child1->size());
  EXPECT_EQ(Size(10, 10), child2->size());
}

TEST_F(FlexLayoutTest, Layout_Flex_OneViewScales_BelowMinimum) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child1 = AddChild(Size(10, 20), Size(5, 5));
  View* child2 = AddChild(Size(10, 10));
  child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);

  host_->SetSize(Size(20, 20));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(10, 5), child1->size());
  EXPECT_EQ(Size(10, 10), child2->size());
}

TEST_F(FlexLayoutTest,
       Layout_Flex_OneViewScales_CausesSubsequentControlToDropOut) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child1 = AddChild(Size(10, 20), Size(5, 5));
  View* child2 = AddChild(Size(10, 10), Size(5, 5));
  child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
  child2->SetProperty(views::kFlexBehaviorKey, kDropOut);

  host_->SetSize(Size(20, 20));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(10, 10), child1->size());
  EXPECT_FALSE(child2->GetVisible());
}

TEST_F(FlexLayoutTest,
       Layout_Flex_OneViewScales_CausesSubsequentFlexControlToDropOut) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child1 = AddChild(Size(10, 20), Size(5, 5));
  View* child2 = AddChild(Size(10, 10), Size(5, 5));
  child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
  child2->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);

  host_->SetSize(Size(20, 19));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(10, 9), child1->size());
  EXPECT_FALSE(child2->GetVisible());
}

TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_EqualWeight) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  layout_->SetDefault(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
  View* child1 = AddChild(Size(20, 10), Size(5, 5));
  View* child2 = AddChild(Size(20, 10), Size(5, 5));

  host_->SetSize(Size(45, 20));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(15, 10), child1->size());
  EXPECT_EQ(Size(15, 10), child2->size());

  host_->SetSize(Size(60, 20));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(20, 10), child1->size());
  EXPECT_EQ(Size(20, 10), child2->size());
}

TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_DefaultFlex) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  layout_->SetDefault(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
  View* child1 = AddChild(Size(20, 10), Size(5, 5));
  View* child2 = AddChild(Size(20, 10), Size(5, 5));

  host_->SetSize(Size(45, 20));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(15, 10), child1->size());
  EXPECT_EQ(Size(15, 10), child2->size());

  host_->SetSize(Size(60, 20));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(20, 10), child1->size());
  EXPECT_EQ(Size(20, 10), child2->size());
}

TEST_F(FlexLayoutTest,
       Layout_Flex_TwoChildViews_UnequalWeight_FirstHigher_FlexSmaller) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child1 = AddChild(Size(20, 10), Size(5, 5));
  View* child2 = AddChild(Size(20, 10), Size(5, 5));
  child1->SetProperty(views::kFlexBehaviorKey, kFlex2ScaleToMinimum);
  child2->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);

  // Deficit of 5 is allocated 2:1, but rounding gives us -3, -2.
  host_->SetSize(Size(50, 20));
  std::vector<gfx::Rect> expected = {{5, 5, 17, 10}, {27, 5, 18, 10}};
  EXPECT_EQ(expected, GetChildBounds());

  // Deficit of 6 divides evenly, gives us -4, -2.
  host_->SetSize(Size(49, 20));
  expected = {{5, 5, 16, 10}, {26, 5, 18, 10}};
  EXPECT_EQ(expected, GetChildBounds());
}

TEST_F(FlexLayoutTest,
       Layout_Flex_TwoChildViews_UnequalWeight_SecondHigher_FlexSmaller) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child1 = AddChild(Size(20, 10), Size(5, 5));
  View* child2 = AddChild(Size(20, 10), Size(5, 5));
  child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
  child2->SetProperty(views::kFlexBehaviorKey, kFlex2ScaleToMinimum);

  // Deficit of 5 is allocated 1:2, but rounding gives us -2, -3.
  host_->SetSize(Size(50, 20));
  std::vector<gfx::Rect> expected = {{5, 5, 18, 10}, {28, 5, 17, 10}};
  EXPECT_EQ(expected, GetChildBounds());

  // Deficit of 6 divides evenly, gives us -2, -4.
  host_->SetSize(Size(49, 20));
  expected = {{5, 5, 18, 10}, {28, 5, 16, 10}};
  EXPECT_EQ(expected, GetChildBounds());
}

// This is a test for the case where one child's flex rule will cause it to
// scale to its minimum size, resulting in the other view getting more space
// than it otherwise would.
TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_UnequalWeight_OneHitsMinimum) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child1 = AddChild(Size(20, 10), Size(5, 5));
  View* child2 = AddChild(Size(20, 10), Size(5, 5));
  child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
  child2->SetProperty(views::kFlexBehaviorKey, kFlex2ScaleToMinimum);

  // Deficit of 20 divides up as -7 and -13.
  host_->SetSize(Size(35, 20));
  std::vector<gfx::Rect> expected = {{5, 5, 13, 10}, {23, 5, 7, 10}};
  EXPECT_EQ(expected, GetChildBounds());

  // Deficit of 25 divides up as -8 and -17, but second view can only shrink by
  // 15, so first view has to shrink by 10 instead.
  host_->SetSize(Size(30, 20));
  expected = {{5, 5, 10, 10}, {20, 5, 5, 10}};
  EXPECT_EQ(expected, GetChildBounds());
}

// This is a test for the case where one child's flex rule will cause it to
// drop out, resulting in the other view getting more space *than its preferred
// size*.
TEST_F(
    FlexLayoutTest,
    Layout_Flex_TwoChildViews_UnequalWeight_OneDropsOut_OtherExceedsPreferred) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child1 = AddChild(Size(20, 10), Size(15, 10));
  View* child2 = AddChild(Size(20, 10), Size(15, 10));
  child1->SetProperty(views::kFlexBehaviorKey,
                      kUnboundedScaleToMinimumSnapToZero);
  child2->SetProperty(views::kFlexBehaviorKey,
                      kUnboundedScaleToMinimumSnapToZero.WithWeight(4));

  host_->SetSize(Size(45, 20));
  std::vector<gfx::Rect> expected = {{5, 5, 35, 10}, {}};
  EXPECT_EQ(expected, GetChildBounds());
}

// This is a regression test for a case where a view marked as having flex
// weight but which could not flex larger than its preferred size would cause
// other views at that weight to not receive available flex space.
TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_FirstViewFillsAvailableSpace) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child1 = AddChild(Size(20, 10));
  View* child2 = AddChild(Size(20, 10));
  child1->SetProperty(views::kFlexBehaviorKey, kUnbounded);
  child2->SetProperty(views::kFlexBehaviorKey,
                      FlexSpecification(MinimumFlexSizeRule::kPreferred,
                                        MaximumFlexSizeRule::kPreferred));

  host_->SetSize(Size(70, 20));
  const std::vector<Rect> expected_bounds = {{5, 5, 35, 10}, {45, 5, 20, 10}};
  EXPECT_EQ(expected_bounds, GetChildBounds());
}

TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_Priority) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child1 = AddChild(Size(20, 10), Size(5, 5));
  View* child2 = AddChild(Size(20, 10), Size(5, 5));
  child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToMinimum);
  child2->SetProperty(views::kFlexBehaviorKey,
                      kFlex1ScaleToMinimumHighPriority);

  host_->SetSize(Size(50, 20));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(15, 10), child1->size());
  EXPECT_EQ(Size(20, 10), child2->size());

  host_->SetSize(Size(35, 20));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(5, 10), child1->size());
  EXPECT_EQ(Size(15, 10), child2->size());
}

TEST_F(FlexLayoutTest,
       Layout_Flex_TwoChildViews_Priority_LowerPriorityDropsOut) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child1 = AddChild(Size(20, 10), Size(5, 5));
  View* child2 = AddChild(Size(20, 10), Size(5, 5));
  child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);
  child2->SetProperty(views::kFlexBehaviorKey,
                      kFlex1ScaleToMinimumHighPriority);

  host_->SetSize(Size(35, 20));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(20, 10), child2->size());
  EXPECT_FALSE(child1->GetVisible());
}

TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedSnapToMinimum) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child = AddChild(Size(20, 10), Size(5, 5));
  child->SetProperty(views::kFlexBehaviorKey, kUnboundedSnapToMinimum);

  host_->SetSize(Size(35, 25));
  EXPECT_EQ(Size(25, 15), child->size());

  host_->SetSize(Size(30, 25));
  EXPECT_EQ(Size(20, 15), child->size());

  host_->SetSize(Size(29, 25));
  EXPECT_EQ(Size(5, 15), child->size());

  host_->SetSize(Size(25, 10));
  EXPECT_EQ(Size(5, 5), child->size());

  // This is actually less space than the child needs, but its flex rule does
  // not allow it to drop out.
  host_->SetSize(Size(10, 10));
  EXPECT_EQ(Size(5, 5), child->size());
}

TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedScaleToMinimumSnapToZero) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child = AddChild(Size(20, 10), Size(5, 5));
  child->SetProperty(views::kFlexBehaviorKey,
                     kUnboundedScaleToMinimumSnapToZero);

  host_->SetSize(Size(35, 25));
  EXPECT_EQ(Size(25, 15), child->size());

  host_->SetSize(Size(30, 25));
  EXPECT_EQ(Size(20, 15), child->size());

  host_->SetSize(Size(29, 25));
  EXPECT_EQ(Size(19, 15), child->size());

  host_->SetSize(Size(25, 16));
  EXPECT_EQ(Size(15, 6), child->size());

  // This is too short to display the view, however it has horizontal size, so
  // the view does not drop out.
  host_->SetSize(Size(25, 10));
  EXPECT_TRUE(child->GetVisible());
  EXPECT_EQ(Size(15, 0), child->size());

  host_->SetSize(Size(15, 15));
  EXPECT_EQ(Size(5, 5), child->size());

  host_->SetSize(Size(14, 15));
  EXPECT_FALSE(child->GetVisible());
}

TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedScaleToZero) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  // Because we are using a flex rule that scales all the way to zero, ensure
  // that the child view's minimum size is *not* respected.
  View* child = AddChild(Size(20, 10), Size(5, 5));
  child->SetProperty(views::kFlexBehaviorKey, kUnboundedScaleToZero);

  host_->SetSize(Size(35, 25));
  EXPECT_EQ(Size(25, 15), child->size());

  host_->SetSize(Size(30, 25));
  EXPECT_EQ(Size(20, 15), child->size());

  host_->SetSize(Size(29, 25));
  EXPECT_EQ(Size(19, 15), child->size());

  host_->SetSize(Size(25, 16));
  EXPECT_EQ(Size(15, 6), child->size());

  // This is too short to display the view, however it has horizontal size, so
  // the view does not drop out.
  host_->SetSize(Size(25, 10));
  EXPECT_TRUE(child->GetVisible());
  EXPECT_EQ(Size(15, 0), child->size());

  host_->SetSize(Size(15, 15));
  EXPECT_EQ(Size(5, 5), child->size());

  host_->SetSize(Size(14, 14));
  EXPECT_EQ(Size(4, 4), child->size());

  host_->SetSize(Size(9, 14));
  EXPECT_FALSE(child->GetVisible());
}

TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedSnapToMinimum1D) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child = AddChild(Size(20, 10), Size(5, 5));
  child->SetProperty(views::kFlexBehaviorKey,
                     kUnboundedSnapToMinimumHorizontal);

  host_->SetSize(Size(35, 25));
  EXPECT_EQ(Size(25, 10), child->size());

  host_->SetSize(Size(30, 25));
  EXPECT_EQ(Size(20, 10), child->size());

  host_->SetSize(Size(29, 25));
  EXPECT_EQ(Size(5, 10), child->size());

  host_->SetSize(Size(25, 10));
  EXPECT_EQ(Size(5, 10), child->size());

  // This is actually less space than the child needs, but its flex rule does
  // not allow it to drop out.
  host_->SetSize(Size(10, 10));
  EXPECT_EQ(Size(5, 10), child->size());
}

TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedScaleToMinimumSnapToZero1D) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child = AddChild(Size(20, 10), Size(5, 5));
  child->SetProperty(views::kFlexBehaviorKey,
                     kUnboundedScaleToMinimumSnapToZeroHorizontal);

  host_->SetSize(Size(35, 25));
  EXPECT_EQ(Size(25, 10), child->size());

  host_->SetSize(Size(30, 25));
  EXPECT_EQ(Size(20, 10), child->size());

  host_->SetSize(Size(29, 25));
  EXPECT_EQ(Size(19, 10), child->size());

  host_->SetSize(Size(25, 16));
  EXPECT_EQ(Size(15, 10), child->size());

  host_->SetSize(Size(25, 10));
  EXPECT_TRUE(child->GetVisible());
  EXPECT_EQ(Size(15, 10), child->size());

  host_->SetSize(Size(15, 15));
  EXPECT_EQ(Size(5, 10), child->size());

  host_->SetSize(Size(14, 15));
  EXPECT_FALSE(child->GetVisible());
}

TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedScaleToZero1D) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  // Because we are using a flex rule that scales all the way to zero, ensure
  // that the child view's minimum size is *not* respected.
  View* child = AddChild(Size(20, 10), Size(5, 5));
  child->SetProperty(views::kFlexBehaviorKey, kUnboundedScaleToZeroHorizontal);

  host_->SetSize(Size(35, 25));
  EXPECT_EQ(Size(25, 10), child->size());

  host_->SetSize(Size(30, 25));
  EXPECT_EQ(Size(20, 10), child->size());

  host_->SetSize(Size(29, 25));
  EXPECT_EQ(Size(19, 10), child->size());

  host_->SetSize(Size(25, 16));
  EXPECT_EQ(Size(15, 10), child->size());

  host_->SetSize(Size(25, 10));
  EXPECT_TRUE(child->GetVisible());
  EXPECT_EQ(Size(15, 10), child->size());

  host_->SetSize(Size(15, 15));
  EXPECT_EQ(Size(5, 10), child->size());

  host_->SetSize(Size(14, 14));
  EXPECT_EQ(Size(4, 10), child->size());

  host_->SetSize(Size(9, 14));
  EXPECT_FALSE(child->GetVisible());
}

// Tests that views allowed to scale up to their maximum size will do so.
TEST_F(FlexLayoutTest, Layout_FlexRule_ScaleToMaximum) {
  auto* const child1 = AddChild(Size(10, 10));
  child1->SetMaximumSize(Size(20, 20));
  child1->SetProperty(kFlexBehaviorKey, kScaleToMaximum);
  auto* const child2 = AddChild(Size(10, 10));
  child2->SetMaximumSize(Size(20, 20));
  child2->SetProperty(kFlexBehaviorKey, kScaleToMaximum);
  auto* const child3 = AddChild(Size(10, 10));
  child3->SetMaximumSize(Size(20, 20));
  child3->SetProperty(kFlexBehaviorKey, kScaleToMaximum);

  host_->SetSize(Size(20, 10));
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected_bounds = {
      {0, 0, 10, 10}, {10, 0, 10, 10}, {20, 0, 10, 10}};
  EXPECT_EQ(expected_bounds, GetChildBounds());

  host_->SetSize(Size(30, 10));
  test::RunScheduledLayout(host_.get());
  expected_bounds = {{0, 0, 10, 10}, {10, 0, 10, 10}, {20, 0, 10, 10}};
  EXPECT_EQ(expected_bounds, GetChildBounds());

  host_->SetSize(Size(33, 10));
  test::RunScheduledLayout(host_.get());
  expected_bounds = {{0, 0, 11, 10}, {11, 0, 11, 10}, {22, 0, 11, 10}};
  EXPECT_EQ(expected_bounds, GetChildBounds());

  host_->SetSize(Size(35, 10));
  test::RunScheduledLayout(host_.get());
  expected_bounds = {{0, 0, 12, 10}, {12, 0, 12, 10}, {24, 0, 11, 10}};
  EXPECT_EQ(expected_bounds, GetChildBounds());

  host_->SetSize(Size(60, 10));
  test::RunScheduledLayout(host_.get());
  expected_bounds = {{0, 0, 20, 10}, {20, 0, 20, 10}, {40, 0, 20, 10}};
  EXPECT_EQ(expected_bounds, GetChildBounds());

  host_->SetSize(Size(70, 10));
  test::RunScheduledLayout(host_.get());
  expected_bounds = {{0, 0, 20, 10}, {20, 0, 20, 10}, {40, 0, 20, 10}};
  EXPECT_EQ(expected_bounds, GetChildBounds());
}

// Tests that views allowed to scale up to their maximum size will do so.
TEST_F(FlexLayoutTest, Layout_FlexRule_ScaleToMaximum_WithOrder) {
  auto* const child1 = AddChild(Size(10, 10));
  child1->SetMaximumSize(Size(20, 20));
  child1->SetProperty(kFlexBehaviorKey, kScaleToMaximum.WithOrder(1));
  auto* const child2 = AddChild(Size(10, 10));
  child2->SetMaximumSize(Size(20, 20));
  child2->SetProperty(kFlexBehaviorKey, kScaleToMaximum.WithOrder(2));
  auto* const child3 = AddChild(Size(10, 10));
  child3->SetMaximumSize(Size(20, 20));
  child3->SetProperty(kFlexBehaviorKey, kScaleToMaximum.WithOrder(3));

  host_->SetSize(Size(20, 10));
  test::RunScheduledLayout(host_.get());
  std::vector<Rect> expected_bounds = {
      {0, 0, 10, 10}, {10, 0, 10, 10}, {20, 0, 10, 10}};
  EXPECT_EQ(expected_bounds, GetChildBounds());

  host_->SetSize(Size(30, 10));
  test::RunScheduledLayout(host_.get());
  expected_bounds = {{0, 0, 10, 10}, {10, 0, 10, 10}, {20, 0, 10, 10}};
  EXPECT_EQ(expected_bounds, GetChildBounds());

  host_->SetSize(Size(33, 10));
  test::RunScheduledLayout(host_.get());
  expected_bounds = {{0, 0, 13, 10}, {13, 0, 10, 10}, {23, 0, 10, 10}};
  EXPECT_EQ(expected_bounds, GetChildBounds());

  host_->SetSize(Size(43, 10));
  test::RunScheduledLayout(host_.get());
  expected_bounds = {{0, 0, 20, 10}, {20, 0, 13, 10}, {33, 0, 10, 10}};
  EXPECT_EQ(expected_bounds, GetChildBounds());

  host_->SetSize(Size(53, 10));
  test::RunScheduledLayout(host_.get());
  expected_bounds = {{0, 0, 20, 10}, {20, 0, 20, 10}, {40, 0, 13, 10}};
  EXPECT_EQ(expected_bounds, GetChildBounds());

  host_->SetSize(Size(70, 10));
  test::RunScheduledLayout(host_.get());
  expected_bounds = {{0, 0, 20, 10}, {20, 0, 20, 10}, {40, 0, 20, 10}};
  EXPECT_EQ(expected_bounds, GetChildBounds());
}

// A higher priority view which can expand past its maximum size should displace
// a lower priority view up to the first view's preferred size.
TEST_F(FlexLayoutTest,
       Layout_FlexRule_TwoPassScaling_PreferredSizeTakesPrecedence) {
  constexpr Size kLargeSize(10, 10);
  constexpr Size kSmallSize(5, 5);
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  View* child1 = AddChild(kLargeSize, kSmallSize);
  child1->SetProperty(views::kFlexBehaviorKey,
                      kUnboundedScaleToMinimumHighPriority);
  View* child2 = AddChild(kSmallSize);
  child2->SetProperty(views::kFlexBehaviorKey, kDropOut);

  // When there is no room for the second view, it drops out.
  host_->SetSize(Size(4, 5));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(kSmallSize, child1->size());
  EXPECT_FALSE(child2->GetVisible());

  // When the first view has less room than its preferred size, it should still
  // take up all of the space.
  constexpr Size kIntermediateSize(8, 7);
  host_->SetSize(kIntermediateSize);
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(kIntermediateSize, child1->size());
  EXPECT_FALSE(child2->GetVisible());

  // When the first view has more room than its preferred size, but not enough
  // to make room for the second view, the second view still drops out.
  constexpr Size kLargerSize(13, 8);
  host_->SetSize(kLargerSize);
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(kLargerSize, child1->size());
  EXPECT_FALSE(child2->GetVisible());
}

// When a view is allowed to flex above its preferred size, it will still yield
// that additional space to a lower-priority view, if there is space for the
// second view.
TEST_F(FlexLayoutTest, Layout_FlexRule_TwoPassScaling_StopAtPreferredSize) {
  constexpr Size kLargeSize(10, 10);
  constexpr Size kSmallSize(5, 5);
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  View* child1 = AddChild(kLargeSize, kSmallSize);
  child1->SetProperty(views::kFlexBehaviorKey,
                      kUnboundedScaleToMinimumHighPriority);
  View* child2 = AddChild(kSmallSize);
  child2->SetProperty(views::kFlexBehaviorKey, kDropOut);

  constexpr Size kEnoughSpace(kSmallSize.width() + kLargeSize.width(),
                              kLargeSize.height());
  host_->SetSize(kEnoughSpace);
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(kLargeSize, child1->size());
  EXPECT_EQ(kSmallSize, child2->size());
}

// Once lower-priority views have reached their preferred sizes, a
// higher-priority view which can expand past its preferred size should start to
// consume the remaining space.
TEST_F(FlexLayoutTest, Layout_FlexRule_TwoPassScaling_GrowPastPreferredSize) {
  constexpr Size kLargeSize(10, 10);
  constexpr Size kSmallSize(5, 5);
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  View* child1 = AddChild(kLargeSize, kSmallSize);
  child1->SetProperty(views::kFlexBehaviorKey,
                      kUnboundedScaleToMinimumHighPriority);
  View* child2 = AddChild(kSmallSize);
  child2->SetProperty(views::kFlexBehaviorKey, kDropOut);

  constexpr int kExtra = 7;
  constexpr Size kExtraSpace(kSmallSize.width() + kLargeSize.width() + kExtra,
                             kLargeSize.height() + kExtra);
  host_->SetSize(kExtraSpace);
  EXPECT_EQ(Size(kLargeSize.width() + kExtra, kLargeSize.height() + kExtra),
            child1->size());
  EXPECT_EQ(kSmallSize, child2->size());
}

// If two views can both scale past their preferred size with the same priority,
// once space has been allocated for each's preferred size, additional space
// will be divided according to flex weight.
TEST_F(FlexLayoutTest,
       Layout_FlexRule_GrowPastPreferredSize_TwoViews_SamePriority) {
  constexpr Size kLargeSize(10, 10);
  constexpr Size kSmallSize(5, 5);
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  // Because we are using a flex rule that scales all the way to zero, ensure
  // that the child view's minimum size is *not* respected.
  View* child1 = AddChild(kLargeSize, kSmallSize);
  child1->SetProperty(views::kFlexBehaviorKey,
                      kUnboundedScaleToMinimumHighPriority);
  View* child2 = AddChild(kLargeSize, kSmallSize);
  child2->SetProperty(views::kFlexBehaviorKey,
                      kUnboundedScaleToMinimumHighPriority);

  constexpr int kExtra = 8;
  constexpr Size kExtraSpace(2 * kLargeSize.width() + kExtra,
                             kLargeSize.height());
  host_->SetSize(kExtraSpace);
  EXPECT_EQ(Size(kLargeSize.width() + kExtra / 2, kLargeSize.height()),
            child1->size());
  EXPECT_EQ(Size(kLargeSize.width() + kExtra / 2, kLargeSize.height()),
            child2->size());
}

// If two views can both scale past their preferred size once space has been
// allocated for each's preferred size, additional space will be given to the
// higher-precedence view.
TEST_F(FlexLayoutTest,
       Layout_FlexRule_GrowPastPreferredSize_TwoViews_DifferentPriority) {
  constexpr Size kLargeSize(10, 10);
  constexpr Size kSmallSize(5, 5);
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  // Because we are using a flex rule that scales all the way to zero, ensure
  // that the child view's minimum size is *not* respected.
  View* child1 = AddChild(kLargeSize, kSmallSize);
  child1->SetProperty(views::kFlexBehaviorKey,
                      kUnboundedScaleToMinimumHighPriority);
  View* child2 = AddChild(kLargeSize, kSmallSize);
  child2->SetProperty(views::kFlexBehaviorKey, kUnboundedScaleToMinimum);

  constexpr int kExtra = 8;
  constexpr Size kExtraSpace(2 * kLargeSize.width() + kExtra,
                             kLargeSize.height());
  host_->SetSize(kExtraSpace);
  EXPECT_EQ(Size(kLargeSize.width() + kExtra, kLargeSize.height()),
            child1->size());
  EXPECT_EQ(kLargeSize, child2->size());
}

TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_FlexAlignment_Start) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child1 = AddChild(Size(10, 10));
  AddChild(Size(10, 10));
  child1->SetProperty(views::kFlexBehaviorKey,
                      kUnbounded.WithAlignment(LayoutAlignment::kStart));

  host_->SetSize(Size(50, 20));
  const std::vector<Rect> expected_bounds = {{5, 5, 10, 10}, {35, 5, 10, 10}};
  EXPECT_EQ(expected_bounds, GetChildBounds());
}

TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_FlexAlignment_End) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child1 = AddChild(Size(10, 10));
  AddChild(Size(10, 10));
  child1->SetProperty(views::kFlexBehaviorKey,
                      kUnbounded.WithAlignment(LayoutAlignment::kEnd));

  host_->SetSize(Size(50, 20));
  const std::vector<Rect> expected_bounds = {{20, 5, 10, 10}, {35, 5, 10, 10}};
  EXPECT_EQ(expected_bounds, GetChildBounds());
}

TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_FlexAlignment_Center) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child1 = AddChild(Size(10, 10));
  AddChild(Size(10, 10));
  child1->SetProperty(views::kFlexBehaviorKey,
                      kUnbounded.WithAlignment(LayoutAlignment::kCenter));

  host_->SetSize(Size(50, 20));
  const std::vector<Rect> expected_bounds = {{12, 5, 10, 10}, {35, 5, 10, 10}};
  EXPECT_EQ(expected_bounds, GetChildBounds());
}

TEST_F(FlexLayoutTest, Layout_FlexRule_CustomFlexRule) {
  constexpr int kFullSize = 50;
  constexpr int kHalfSize = 25;

  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child = AddChild(Size(kFullSize, kFullSize));
  child->SetProperty(views::kFlexBehaviorKey, kCustomFlex);

  host_->SetSize(Size(100, 100));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(kFullSize, kFullSize), child->size());

  host_->SetSize(Size(100, 50));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(kFullSize, kHalfSize), child->size());

  host_->SetSize(Size(50, 100));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(kHalfSize, kFullSize), child->size());

  host_->SetSize(Size(45, 40));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(kHalfSize, kHalfSize), child->size());

  // Custom flex rule does not go below half size.
  host_->SetSize(Size(20, 20));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(kHalfSize, kHalfSize), child->size());
}

TEST_F(FlexLayoutTest, Layout_FlexRule_CustomFlexRule_WithNonFlex) {
  constexpr int kFullSize = 50;
  constexpr int kHalfSize = 25;

  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child = AddChild(Size(kFullSize, kFullSize));
  AddChild(Size(10, 10));
  child->SetProperty(views::kFlexBehaviorKey, kCustomFlex);

  host_->SetSize(Size(100, 100));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(kFullSize, kFullSize), child->size());

  host_->SetSize(Size(100, 65));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(kFullSize, kHalfSize), child->size());

  host_->SetSize(Size(50, 100));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(kHalfSize, kFullSize), child->size());

  host_->SetSize(Size(45, 40));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(kHalfSize, kHalfSize), child->size());
}

TEST_F(FlexLayoutTest, Layout_FlexRule_CustomFlexRule_ShrinkToZero) {
  constexpr int kFullSize = 50;
  constexpr int kHalfSize = 25;

  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  View* child = AddChild(Size(kFullSize, kFullSize));
  child->SetProperty(views::kFlexBehaviorKey, kCustomFlexSnapToZero);

  host_->SetSize(Size(100, 100));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(kFullSize, kFullSize), child->size());

  host_->SetSize(Size(100, 50));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(kFullSize, kHalfSize), child->size());

  host_->SetSize(Size(50, 100));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(kHalfSize, kFullSize), child->size());

  host_->SetSize(Size(45, 40));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(Size(kHalfSize, kHalfSize), child->size());

  host_->SetSize(Size(20, 20));
  test::RunScheduledLayout(host_.get());
  EXPECT_FALSE(child->GetVisible());
}

TEST_F(FlexLayoutTest, Layout_OnlyCallsSetViewVisibilityWhenNecessary) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetInteriorMargin(Insets(5));
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  MockView* child1 = AddChild(Size(20, 10), Size(5, 5));
  MockView* child2 = AddChild(Size(20, 10), Size(5, 5));
  child1->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);
  child2->SetProperty(views::kFlexBehaviorKey,
                      kFlex1ScaleToMinimumHighPriority);

  child1->ResetCounts();
  child2->ResetCounts();
  host_->SetSize(Size(40, 20));
  test::RunScheduledLayout(host_.get());
  EXPECT_TRUE(child1->GetVisible());
  EXPECT_TRUE(child2->GetVisible());
  EXPECT_EQ(0, child1->GetSetVisibleCount());
  EXPECT_EQ(0, child2->GetSetVisibleCount());

  host_->SetSize(Size(35, 20));
  test::RunScheduledLayout(host_.get());
  EXPECT_FALSE(child1->GetVisible());
  EXPECT_TRUE(child2->GetVisible());
  EXPECT_EQ(1, child1->GetSetVisibleCount());
  EXPECT_EQ(0, child2->GetSetVisibleCount());

  child1->ResetCounts();
  child2->ResetCounts();
  host_->SetSize(Size(30, 20));
  test::RunScheduledLayout(host_.get());
  EXPECT_FALSE(child1->GetVisible());
  EXPECT_TRUE(child2->GetVisible());
  EXPECT_EQ(0, child1->GetSetVisibleCount());
  EXPECT_EQ(0, child2->GetSetVisibleCount());

  child1->SetVisible(false);
  child1->ResetCounts();

  host_->SetSize(Size(40, 20));
  test::RunScheduledLayout(host_.get());
  EXPECT_FALSE(child1->GetVisible());
  EXPECT_TRUE(child2->GetVisible());
  EXPECT_EQ(0, child1->GetSetVisibleCount());
  EXPECT_EQ(0, child2->GetSetVisibleCount());
}

TEST_F(FlexLayoutTest, Layout_Vertical_ZeroWidthNonZeroHeight) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kCenter);
  layout_->SetDefault(kMarginsKey, gfx::Insets(5));
  layout_->SetCollapseMargins(true);
  AddChild(gfx::Size(10, 10));
  AddChild(gfx::Size(0, 10));
  AddChild(gfx::Size(10, 10));

  host_->SetSize(gfx::Size(20, 50));
  const std::vector<gfx::Rect> expected = {
      {5, 5, 10, 10}, {10, 20, 0, 10}, {5, 35, 10, 10}};
  EXPECT_EQ(expected, GetChildBounds());
}

TEST_F(FlexLayoutTest, Layout_Vertical_ZeroWidthZeroHeight) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kCenter);
  layout_->SetDefault(kMarginsKey, gfx::Insets(5));
  layout_->SetCollapseMargins(true);
  AddChild(gfx::Size(10, 10));
  AddChild(gfx::Size(0, 0));
  AddChild(gfx::Size(10, 10));

  host_->SetSize(gfx::Size(20, 40));
  const std::vector<gfx::Rect> expected = {{5, 5, 10, 10}, {}, {5, 20, 10, 10}};
  EXPECT_EQ(expected, GetChildBounds());
}

// Available Size Tests -------------------------------------------------------

TEST_F(FlexLayoutTest, GetAvailableSize_NoFlex) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  MockView* const child1 = AddChild(Size(20, 10));
  MockView* const child2 = AddChild(Size(10, 5));

  // With no flex at preferred size views will get their preferred main axis
  // size.
  host_->SizeToPreferredSize();
  EXPECT_EQ(SizeBounds(20, 10), host_->GetAvailableSize(child1));
  EXPECT_EQ(SizeBounds(10, 10), host_->GetAvailableSize(child2));
}

TEST_F(FlexLayoutTest, GetAvailableSize_NoFlex_Margins) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  MockView* const child1 = AddChild(Size(20, 10));
  child1->SetProperty(views::kMarginsKey, gfx::Insets::TLBR(3, 5, 7, 5));
  MockView* const child2 = AddChild(Size(10, 5));
  child2->SetProperty(views::kMarginsKey, gfx::Insets::TLBR(9, 5, 5, 5));

  host_->SizeToPreferredSize();
  EXPECT_EQ(SizeBounds(20, 10), host_->GetAvailableSize(child1));
  EXPECT_EQ(SizeBounds(10, 6), host_->GetAvailableSize(child2));
}

TEST_F(FlexLayoutTest, GetAvailableSize_NoFlex_ExtraSize) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  MockView* const child1 = AddChild(Size(20, 10));
  MockView* const child2 = AddChild(Size(10, 5));

  host_->SetSize({50, 25});
  const int excess = host_->width() - host_->GetPreferredSize({}).width();
  EXPECT_EQ(SizeBounds(child1->GetPreferredSize({}).width() + excess, 15),
            host_->GetAvailableSize(child1));
  EXPECT_EQ(SizeBounds(child2->GetPreferredSize({}).width() + excess, 15),
            host_->GetAvailableSize(child2));
}

TEST_F(FlexLayoutTest, GetAvailableSize_NoFlex_Vertical) {
  layout_->SetOrientation(LayoutOrientation::kVertical);
  layout_->SetCollapseMargins(true);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  MockView* const child1 = AddChild(Size(20, 10));
  MockView* const child2 = AddChild(Size(10, 5));

  host_->SetSize({35, 35});
  const int excess = host_->height() - host_->GetPreferredSize({}).height();
  EXPECT_EQ(SizeBounds(25, child1->GetPreferredSize({}).height() + excess),
            host_->GetAvailableSize(child1));
  EXPECT_EQ(SizeBounds(25, child2->GetPreferredSize({}).height() + excess),
            host_->GetAvailableSize(child2));
}

TEST_F(FlexLayoutTest, GetAvailableSize_Flex_AllSameSize) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  MockView* const child1 = AddChild(Size(20, 10));
  MockView* const child2 = AddChild(Size(10, 5));
  MockView* const child3 = AddChild(Size(5, 5));
  child3->SetProperty(kFlexBehaviorKey, kUnboundedScaleToZero);
  MockView* const child4 = AddChild(Size(5, 5));
  child4->SetProperty(kFlexBehaviorKey, kUnboundedScaleToZero);

  host_->SizeToPreferredSize();
  // Each of these views can expand into both the view space and the margins for
  // the third and fourth views (total 10 each).
  EXPECT_EQ(SizeBounds(40, 10), host_->GetAvailableSize(child1));
  EXPECT_EQ(SizeBounds(30, 10), host_->GetAvailableSize(child2));
  // Each of these can expand into the view space and margin of the other.
  EXPECT_EQ(SizeBounds(15, 10), host_->GetAvailableSize(child3));
  EXPECT_EQ(SizeBounds(15, 10), host_->GetAvailableSize(child4));

  // At minimum size there should be no excess available.
  host_->SetSize(host_->GetMinimumSize());
  EXPECT_EQ(SizeBounds(20, 10), host_->GetAvailableSize(child1));
  EXPECT_EQ(SizeBounds(10, 10), host_->GetAvailableSize(child2));
  EXPECT_EQ(SizeBounds(0, 10), host_->GetAvailableSize(child3));
  EXPECT_EQ(SizeBounds(0, 10), host_->GetAvailableSize(child4));
}

TEST_F(FlexLayoutTest, GetAvailableSize_Flex_VariedMinimumSizes) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  MockView* const child1 = AddChild(Size(20, 10));
  MockView* const child2 = AddChild(Size(10, 5));
  MockView* const child3 = AddChild(Size(10, 10), Size(7, 7));
  child3->SetProperty(kFlexBehaviorKey, kUnboundedScaleToMinimum);
  MockView* const child4 = AddChild(Size(12, 12), Size(5, 5));
  child4->SetProperty(kFlexBehaviorKey, kUnboundedScaleToMinimum);

  host_->SizeToPreferredSize();
  // Since the third and fourth views can only shrink a certain amount, the
  // excess available to the first two views is smaller than in previous tests.
  EXPECT_EQ(SizeBounds(30, 12), host_->GetAvailableSize(child1));
  EXPECT_EQ(SizeBounds(20, 12), host_->GetAvailableSize(child2));
  // Each of these can only consume the difference between the other's minimum
  // and preferred sizes (as per the flex rule).
  EXPECT_EQ(SizeBounds(17, 12), host_->GetAvailableSize(child3));
  EXPECT_EQ(SizeBounds(15, 12), host_->GetAvailableSize(child4));

  // At minimum size there should be no excess available.
  host_->SetSize(host_->GetMinimumSize());
  EXPECT_EQ(SizeBounds(20, 10), host_->GetAvailableSize(child1));
  EXPECT_EQ(SizeBounds(10, 10), host_->GetAvailableSize(child2));
  EXPECT_EQ(SizeBounds(7, 10), host_->GetAvailableSize(child3));
  EXPECT_EQ(SizeBounds(5, 10), host_->GetAvailableSize(child4));
}

TEST_F(FlexLayoutTest, GetAvailableSize_Flex_HiddenViews) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  MockView* const child1 = AddChild(Size(20, 10));
  MockView* const child2 = AddChild(Size(10, 5));
  MockView* const child3 = AddChild(Size(5, 5));
  child3->SetProperty(kFlexBehaviorKey, kDropOut);
  MockView* const child4 = AddChild(Size(5, 5));
  child4->SetProperty(kFlexBehaviorKey, kDropOut);

  // Make the second child invisible. This should exclude it from the layout.
  child2->SetVisible(false);

  host_->SizeToPreferredSize();
  // This view can expand into both the view space and the margins for the third
  // and fourth views (total 10 each).
  EXPECT_EQ(SizeBounds(40, 10), host_->GetAvailableSize(child1));
  EXPECT_EQ(SizeBounds(), host_->GetAvailableSize(child2));
  // These views can expand into each others' space.
  EXPECT_EQ(SizeBounds(15, 10), host_->GetAvailableSize(child3));
  EXPECT_EQ(SizeBounds(15, 10), host_->GetAvailableSize(child4));

  // This should cause one of the third or fourth children to drop out, but both
  // will still have some space available.
  host_->SetSize({40, 20});
  EXPECT_EQ(SizeBounds(30, 10), host_->GetAvailableSize(child1));
  EXPECT_EQ(SizeBounds(), host_->GetAvailableSize(child2));
  EXPECT_EQ(SizeBounds(5, 10), host_->GetAvailableSize(child3));
  EXPECT_EQ(SizeBounds(5, 10), host_->GetAvailableSize(child4));

  // At minimum size, there is no space for the third or fourth view.
  host_->SetSize(host_->GetMinimumSize());
  EXPECT_EQ(SizeBounds(20, 10), host_->GetAvailableSize(child1));
  EXPECT_EQ(SizeBounds(), host_->GetAvailableSize(child2));
  EXPECT_EQ(SizeBounds(0, 10), host_->GetAvailableSize(child3));
  EXPECT_EQ(SizeBounds(0, 10), host_->GetAvailableSize(child4));
}

TEST_F(FlexLayoutTest, GetAvailableSize_Flex_DifferentWeights) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCollapseMargins(true);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  layout_->SetDefault(views::kMarginsKey, gfx::Insets(5));
  MockView* const child1 = AddChild(Size(20, 10));
  MockView* const child2 = AddChild(Size(10, 5));
  MockView* const child3 = AddChild(Size(12, 12), Size(7, 7));
  child3->SetProperty(kFlexBehaviorKey, kFlex1ScaleToMinimumHighPriority);
  MockView* const child4 = AddChild(Size(8, 8), Size(4, 4));
  child4->SetProperty(kFlexBehaviorKey, kFlex1ScaleToMinimum);

  host_->SetSize({80, 25});
  const int excess = host_->width() - host_->GetPreferredSize({}).width();
  const int child3_excess =
      child3->GetPreferredSize({}).width() - child3->GetMinimumSize().width();
  const int child4_excess =
      child4->GetPreferredSize({}).width() - child4->GetMinimumSize().width();
  // The first two views can take all of the excess plus the difference between
  // minimum and preferred size for each of the third and fourth views.
  EXPECT_EQ(SizeBounds(child1->GetPreferredSize({}).width() + excess +
                           child3_excess + child4_excess,
                       15),
            host_->GetAvailableSize(child1));
  EXPECT_EQ(SizeBounds(child2->GetPreferredSize({}).width() + excess +
                           child3_excess + child4_excess,
                       15),
            host_->GetAvailableSize(child2));
  // The third view has a higher priority, so it can take the excess plus the
  // excess from the fourth view.
  EXPECT_EQ(
      SizeBounds(child3->GetPreferredSize({}).width() + excess + child4_excess,
                 15),
      host_->GetAvailableSize(child3));
  // This view has the lowest priority so it can only take the excess space in
  // the layout.
  EXPECT_EQ(SizeBounds(child4->GetPreferredSize({}).width() + excess, 15),
            host_->GetAvailableSize(child4));

  // Same as above, but there is no overall excess.
  host_->SizeToPreferredSize();
  EXPECT_EQ(SizeBounds(child1->GetPreferredSize({}).width() + child3_excess +
                           child4_excess,
                       12),
            host_->GetAvailableSize(child1));
  EXPECT_EQ(SizeBounds(child2->GetPreferredSize({}).width() + child3_excess +
                           child4_excess,
                       12),
            host_->GetAvailableSize(child2));
  EXPECT_EQ(
      SizeBounds(child3->GetPreferredSize({}).width() + child4_excess, 12),
      host_->GetAvailableSize(child3));
  EXPECT_EQ(SizeBounds(child4->GetPreferredSize({}).width(), 12),
            host_->GetAvailableSize(child4));

  // At minimum size there is no excess; all views get their minimum size.
  host_->SetSize(host_->GetMinimumSize());
  EXPECT_EQ(SizeBounds(child1->GetPreferredSize({}).width(), 10),
            host_->GetAvailableSize(child1));
  EXPECT_EQ(SizeBounds(child2->GetPreferredSize({}).width(), 10),
            host_->GetAvailableSize(child2));
  EXPECT_EQ(SizeBounds(child3->GetMinimumSize().width(), 10),
            host_->GetAvailableSize(child3));
  EXPECT_EQ(SizeBounds(child4->GetMinimumSize().width(), 10),
            host_->GetAvailableSize(child4));
}

// Flex Allocation Order -------------------------------------------------------

TEST_F(FlexLayoutTest, FlexAllocationOrderNormal) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetDefault(kFlexBehaviorKey, kDropOut.WithWeight(0));
  layout_->SetFlexAllocationOrder(FlexAllocationOrder::kNormal);
  View* const v1 = AddChild({10, 10});
  View* const v2 = AddChild({10, 10});
  View* const v3 = AddChild({10, 10});

  host_->SetSize({35, 10});
  EXPECT_TRUE(v1->GetVisible());
  EXPECT_TRUE(v2->GetVisible());
  EXPECT_TRUE(v3->GetVisible());

  host_->SetSize({25, 10});
  EXPECT_TRUE(v1->GetVisible());
  EXPECT_TRUE(v2->GetVisible());
  EXPECT_FALSE(v3->GetVisible());

  host_->SetSize({15, 10});
  EXPECT_TRUE(v1->GetVisible());
  EXPECT_FALSE(v2->GetVisible());
  EXPECT_FALSE(v3->GetVisible());
}

TEST_F(FlexLayoutTest, FlexAllocationOrderReverse) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetDefault(kFlexBehaviorKey, kDropOut.WithWeight(0));
  layout_->SetFlexAllocationOrder(FlexAllocationOrder::kReverse);
  View* const v1 = AddChild({10, 10});
  View* const v2 = AddChild({10, 10});
  View* const v3 = AddChild({10, 10});

  host_->SetSize({35, 10});
  EXPECT_TRUE(v1->GetVisible());
  EXPECT_TRUE(v2->GetVisible());
  EXPECT_TRUE(v3->GetVisible());

  host_->SetSize({25, 10});
  EXPECT_FALSE(v1->GetVisible());
  EXPECT_TRUE(v2->GetVisible());
  EXPECT_TRUE(v3->GetVisible());

  host_->SetSize({15, 10});
  EXPECT_FALSE(v1->GetVisible());
  EXPECT_FALSE(v2->GetVisible());
  EXPECT_TRUE(v3->GetVisible());
}

TEST_F(FlexLayoutTest, FlexAllocationOrderNormalWithExcess) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetDefault(kFlexBehaviorKey, kUnbounded.WithWeight(0));
  layout_->SetFlexAllocationOrder(FlexAllocationOrder::kNormal);
  View* const v1 = AddChild({10, 10});
  View* const v2 = AddChild({10, 10});
  View* const v3 = AddChild({10, 10});

  host_->SetSize({30, 10});
  EXPECT_EQ(gfx::Size(10, 10), v1->size());
  EXPECT_EQ(gfx::Size(10, 10), v2->size());
  EXPECT_EQ(gfx::Size(10, 10), v3->size());

  host_->SetSize({50, 10});
  EXPECT_EQ(gfx::Size(30, 10), v1->size());
  EXPECT_EQ(gfx::Size(10, 10), v2->size());
  EXPECT_EQ(gfx::Size(10, 10), v3->size());
}

TEST_F(FlexLayoutTest, FlexAllocationOrderReverseWithExcess) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetDefault(kFlexBehaviorKey, kUnbounded.WithWeight(0));
  layout_->SetFlexAllocationOrder(FlexAllocationOrder::kReverse);
  View* const v1 = AddChild({10, 10});
  View* const v2 = AddChild({10, 10});
  View* const v3 = AddChild({10, 10});

  host_->SetSize({30, 10});
  EXPECT_EQ(gfx::Size(10, 10), v1->size());
  EXPECT_EQ(gfx::Size(10, 10), v2->size());
  EXPECT_EQ(gfx::Size(10, 10), v3->size());

  host_->SetSize({50, 10});
  EXPECT_EQ(gfx::Size(10, 10), v1->size());
  EXPECT_EQ(gfx::Size(10, 10), v2->size());
  EXPECT_EQ(gfx::Size(30, 10), v3->size());
}

// Specific Regression Cases ---------------------------------------------------

// Test case (and example code) for crbug.com/1012119:
// "FlexLayout ignores custom flex rule if it contradicts preferred size"
TEST_F(FlexLayoutTest, FlexRuleContradictsPreferredSize) {
  const FlexSpecification custom_spec(
      base::BindRepeating([](const View* view, const SizeBounds& maximum_size) {
        return gfx::Size((maximum_size.width() >= 100) ? 100 : 0, 100);
      }));

  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
  View* const v1 = AddChild(gfx::Size(7, 7));
  View* const v2 = AddChild(gfx::Size(7, 7));
  v1->SetProperty(kFlexBehaviorKey, custom_spec.WithOrder(1));
  v2->SetProperty(kFlexBehaviorKey, kUnboundedScaleToZero);

  host_->SetSize({200, 100});
  std::vector<gfx::Rect> expected = {{0, 0, 100, 100}, {100, 0, 100, 100}};
  EXPECT_EQ(expected, GetChildBounds());

  host_->SetSize({101, 100});
  expected = {{0, 0, 100, 100}, {100, 0, 1, 100}};
  EXPECT_EQ(expected, GetChildBounds());

  host_->SetSize({100, 100});
  expected = {{0, 0, 100, 100}, {}};
  EXPECT_EQ(expected, GetChildBounds());

  host_->SetSize({99, 100});
  expected = {{}, {0, 0, 99, 100}};
  EXPECT_EQ(expected, GetChildBounds());

  host_->SetSize({1, 100});
  expected = {{}, {0, 0, 1, 100}};
  EXPECT_EQ(expected, GetChildBounds());
}

// Test case (and example code) for crbug.com/1012136:
// "FlexLayout makes children with preferred main axis size 0 invisible even if
//  they are kUnbounded"
TEST_F(FlexLayoutTest, PreferredSizeZeroPreventsFlex_Horizontal) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal);

  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  AddChild(gfx::Size(10, 10));
  View* const v1 = AddChild(gfx::Size(0, 10));
  View* const v2 = AddChild(gfx::Size(0, 10));
  v1->SetProperty(kFlexBehaviorKey, kUnboundedScaleToZero);
  v2->SetProperty(kFlexBehaviorKey, kUnboundedSnapToZero);

  host_->SetSize({30, 15});
  std::vector<gfx::Rect> expected = {
      {0, 0, 10, 10}, {10, 0, 10, 15}, {20, 0, 10, 15}};
  EXPECT_EQ(expected, GetChildBounds());

  layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
  test::RunScheduledLayout(host_.get());
  expected = {{0, 0, 10, 15}, {10, 0, 10, 15}, {20, 0, 10, 15}};
  EXPECT_EQ(expected, GetChildBounds());
}

// Test case (and example code) for crbug.com/1012136:
// "FlexLayout makes children with preferred main axis size 0 invisible even if
//  they are kUnbounded"
TEST_F(FlexLayoutTest, PreferredSizeZeroPreventsFlex_Vertical) {
  layout_->SetOrientation(LayoutOrientation::kVertical);

  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  AddChild(gfx::Size(10, 10));
  View* const v1 = AddChild(gfx::Size(10, 0));
  View* const v2 = AddChild(gfx::Size(10, 0));
  v1->SetProperty(kFlexBehaviorKey, kUnboundedScaleToZero);
  v2->SetProperty(kFlexBehaviorKey, kUnboundedSnapToZero);

  host_->SetSize({15, 30});
  std::vector<gfx::Rect> expected = {
      {0, 0, 10, 10}, {0, 10, 15, 10}, {0, 20, 15, 10}};
  EXPECT_EQ(expected, GetChildBounds());

  layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
  test::RunScheduledLayout(host_.get());
  expected = {{0, 0, 15, 10}, {0, 10, 15, 10}, {0, 20, 15, 10}};
  EXPECT_EQ(expected, GetChildBounds());
}

// Test case should be fixed by:
// https://chromium-review.googlesource.com/c/chromium/src/+/2420128
// Specifically, a label in a flex layout should report preferred height of
// |max_lines| * |line_height| when width is zero, which should affect the
// height bound of the layout.
TEST_F(FlexLayoutTest, LabelPreferredHeightChangesWithWidth) {
  // A LayoutProvider must exist in scope in order to create a Label.
  LayoutProvider layout_provider;

  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  AddChild(gfx::Size(20, 20));
  Label* const label = host_->AddChildView(std::make_unique<Label>());
  label->SetMultiLine(true);
  label->SetMaxLines(2);
  label->SetLineHeight(20);
  label->SetProperty(kFlexBehaviorKey,
                     FlexSpecification(LayoutOrientation::kHorizontal,
                                       MinimumFlexSizeRule::kScaleToZero,
                                       MaximumFlexSizeRule::kPreferred,
                                       /*use_height_for_width*/ true));
  // Use a long text string with lots of small words that will wrap.
  label->SetText(u"The quick brown fox jumps over the lazy dogs.");

  // Verify that when there is enough space, the label takes up a single line.
  host_->SizeToPreferredSize();
  EXPECT_EQ(20, host_->height());
  EXPECT_EQ(20, label->height());

  // Now shrink the layout enough that the line should wrap.
  const int new_width = host_->width() - 20;
  const int new_height = host_->GetHeightForWidth(new_width);
  EXPECT_EQ(new_height, 40);
  host_->SetSize(gfx::Size(new_width, new_height));
  EXPECT_EQ(40, label->height());
}

// Regression test for crbug.com/1239888:
// A vertical layout nested in a horizontal layout should be laid out properly.
// Specifically, it should get its full height if its child views need to grow
// vertically if they are compressed horizontally.
TEST_F(FlexLayoutTest, VerticalInHorizontalInVertical_HeightForWidth) {
  constexpr gfx::Size kChildSize(10, 10);
  auto* const horizontal = host_->AddChildView(std::make_unique<views::View>());
  auto* view1 = AddChild(horizontal, kChildSize);
  auto* const vertical =
      horizontal->AddChildView(std::make_unique<views::View>());
  auto* const view2 = AddChild(vertical, kChildSize);
  view2->set_size_mode(MockView::SizeMode::kFixedArea);
  auto* const view3 = AddChild(vertical, kChildSize);
  view3->set_size_mode(MockView::SizeMode::kFixedArea);
  auto* const view4 = AddChild(kChildSize);

  layout_->SetOrientation(LayoutOrientation::kVertical);
  horizontal->SetProperty(
      kFlexBehaviorKey,
      FlexSpecification(
          horizontal->SetLayoutManager(std::make_unique<FlexLayout>())
              ->SetOrientation(LayoutOrientation::kHorizontal)
              .SetCrossAxisAlignment(LayoutAlignment::kStart)
              .GetDefaultFlexRule()));
  vertical->SetProperty(
      kFlexBehaviorKey,
      FlexSpecification(
          vertical->SetLayoutManager(std::make_unique<FlexLayout>())
              ->SetOrientation(LayoutOrientation::kVertical)
              .GetDefaultFlexRule()));

  const views::FlexSpecification view_flex(
      views::LayoutOrientation::kVertical,
      views::MinimumFlexSizeRule::kPreferred,
      views::MaximumFlexSizeRule::kPreferred,
      /* adjust_height_for_width = */ true,
      views::MinimumFlexSizeRule::kScaleToZero);
  view2->SetProperty(kFlexBehaviorKey, view_flex);
  view3->SetProperty(kFlexBehaviorKey, view_flex);

  EXPECT_EQ(gfx::Size(20, 30), host_->GetPreferredSize({}));
  EXPECT_EQ(40, horizontal->GetHeightForWidth(15));
  EXPECT_EQ(50, host_->GetHeightForWidth(15));

  host_->SetSize(gfx::Size(15, 50));
  EXPECT_EQ(gfx::Rect(0, 0, 15, 40), horizontal->bounds());
  EXPECT_EQ(gfx::Rect(0, 40, 15, 10), view4->bounds());
  EXPECT_EQ(gfx::Rect(0, 0, 10, 10), view1->bounds());
  EXPECT_EQ(gfx::Rect(10, 0, 5, 40), vertical->bounds());
  EXPECT_EQ(gfx::Rect(0, 0, 5, 20), view2->bounds());
  EXPECT_EQ(gfx::Rect(0, 20, 5, 20), view3->bounds());
}

// Pixel-Perfect/Advanced Tests ------------------------------------------------

// NOTE: these are tests ensuring the quasi-multipass behavior of FlexLayout
// (i.e. special-case handling when views do not take up exactly as much space
// as they are offered and need to have the excess redistributed).

namespace {

// Flex rule that steps each dimension by 5.
Size StepwiseFlexRule(int step,
                      const View* view,
                      const SizeBounds& maximum_size) {
  DCHECK_GT(step, 0);
  Size preferred = view->GetPreferredSize({});
  if (maximum_size.width().is_bounded()) {
    const int rounded = step * (maximum_size.width().value() / step);
    preferred.SetToMax({rounded, 0});
  }
  if (maximum_size.height().is_bounded()) {
    const int rounded = step * (maximum_size.height().value() / step);
    preferred.SetToMax({0, rounded});
  }
  return preferred;
}

}  // namespace

// When a view does not take all of the space granted it when excess space is
// being distributed (views flexing above their preferred size) the remaining
// space should be distributed to other views at that flex order.
TEST_F(FlexLayoutTest, Advanced_ViewDoesNotTakeFullExcess_Reallocation) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal)
      .SetCrossAxisAlignment(LayoutAlignment::kStart)
      .SetCollapseMargins(true)
      .SetDefault(kMarginsKey, gfx::Insets(5));
  View* const v1 = AddChild(gfx::Size(10, 10));
  View* const v2 = AddChild(gfx::Size(10, 10));

  // By putting the view that expands stepwise after the one that can scale
  // smoothly but at the same priority, we test the ability of the layout to
  // detect that the stepwise view isn't using all of its space and
  // redistribute that back to the first view.
  v1->SetProperty(kFlexBehaviorKey,
                  FlexSpecification(MinimumFlexSizeRule::kPreferred,
                                    MaximumFlexSizeRule::kUnbounded));
  v2->SetProperty(kFlexBehaviorKey,
                  FlexSpecification(base::BindRepeating(&StepwiseFlexRule, 5)));

  // All views at preferred size.
  host_->SetSize({35, 20});
  std::vector<gfx::Rect> expected = {{5, 5, 10, 10}, {20, 5, 10, 10}};
  EXPECT_EQ(expected, GetChildBounds());

  // Small increase goes to the first view since the second can't use it.
  host_->SetSize({37, 20});
  expected = {{5, 5, 12, 10}, {22, 5, 10, 10}};
  EXPECT_EQ(expected, GetChildBounds());

  // This would be enough to step up the second view but it needs to be
  // distributed over both views, so the second view still can't use it.
  host_->SetSize({40, 20});
  expected = {{5, 5, 15, 10}, {25, 5, 10, 10}};
  EXPECT_EQ(expected, GetChildBounds());

  // There is finally enough space to allocate to the second view that it can
  // increase its size.
  host_->SetSize({45, 20});
  expected = {{5, 5, 15, 10}, {25, 5, 15, 10}};
  EXPECT_EQ(expected, GetChildBounds());
}

// When preferred size of views is zero and adding in zero-size views in the
// "allocate excess flex space" phase would put us above the total available
// size, none of them should show.
TEST_F(FlexLayoutTest, Advanced_PreferredSizeZero_AllOrNothing) {
  const FlexSpecification spec_scale = FlexSpecification(
      MinimumFlexSizeRule::kScaleToZero, MaximumFlexSizeRule::kUnbounded);

  layout_->SetOrientation(LayoutOrientation::kVertical)
      .SetCrossAxisAlignment(LayoutAlignment::kStart)
      .SetCollapseMargins(true)
      .SetDefault(kFlexBehaviorKey, spec_scale)
      .SetDefault(kMarginsKey, gfx::Insets(5));
  View* const v1 = AddChild(gfx::Size(10, 0));
  View* const v2 = AddChild(gfx::Size(10, 0));

  host_->SetSize({20, 15});
  EXPECT_FALSE(v1->GetVisible());
  EXPECT_FALSE(v2->GetVisible());
  std::vector<gfx::Rect> expected = {{}, {}};
  EXPECT_EQ(expected, GetChildBounds());

  host_->SetSize({20, 16});
  EXPECT_FALSE(v1->GetVisible());
  EXPECT_FALSE(v2->GetVisible());
  EXPECT_EQ(expected, GetChildBounds());

  host_->SetSize({20, 17});
  EXPECT_TRUE(v1->GetVisible());
  EXPECT_TRUE(v2->GetVisible());
  expected = {{5, 5, 10, 1}, {5, 11, 10, 1}};
  EXPECT_EQ(expected, GetChildBounds());
}

// Individual cross-axis alignment test ----------------------------------------

TEST_F(FlexLayoutTest, IndividualCrossAxisAlignmentInHorizontalLayoutTest) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal)
      .SetDefault(kMarginsKey, gfx::Insets(5));
  View* const v1 = AddChild(gfx::Size(10, 10));
  v1->SetProperty(kCrossAxisAlignmentKey, LayoutAlignment::kStart);
  View* const v2 = AddChild(gfx::Size(10, 10));
  v2->SetProperty(kCrossAxisAlignmentKey, LayoutAlignment::kCenter);
  View* const v3 = AddChild(gfx::Size(10, 10));
  v3->SetProperty(kCrossAxisAlignmentKey, LayoutAlignment::kEnd);
  View* const v4 = AddChild(gfx::Size(10, 10));
  v4->SetProperty(kCrossAxisAlignmentKey, LayoutAlignment::kStretch);
  View* const v5 = AddChild(gfx::Size(10, 10));
  // v5 uses default

  host_->SizeToPreferredSize();
  EXPECT_EQ(5, v1->y());
  EXPECT_EQ(10, v1->height());
  EXPECT_EQ(5, v2->y());
  EXPECT_EQ(10, v2->height());
  EXPECT_EQ(5, v3->y());
  EXPECT_EQ(10, v3->height());
  EXPECT_EQ(5, v4->y());
  EXPECT_EQ(10, v4->height());
  EXPECT_EQ(5, v5->y());
  EXPECT_EQ(10, v5->height());

  // Next try a larger view.
  host_->SetSize(gfx::Size(100, 30));
  EXPECT_EQ(5, v1->y());
  EXPECT_EQ(10, v1->height());
  EXPECT_EQ(10, v2->y());
  EXPECT_EQ(10, v2->height());
  EXPECT_EQ(15, v3->y());
  EXPECT_EQ(10, v3->height());
  EXPECT_EQ(5, v4->y());
  EXPECT_EQ(20, v4->height());
  EXPECT_EQ(5, v5->y());
  EXPECT_EQ(20, v5->height());

  // Move to a smaller view.
  host_->SetSize(gfx::Size(100, 12));
  EXPECT_EQ(5, v1->y());
  EXPECT_EQ(10, v1->height());
  EXPECT_EQ(1, v2->y());
  EXPECT_EQ(10, v2->height());
  EXPECT_EQ(-3, v3->y());
  EXPECT_EQ(10, v3->height());
  EXPECT_EQ(5, v4->y());
  EXPECT_EQ(2, v4->height());
  EXPECT_EQ(5, v5->y());
  EXPECT_EQ(2, v5->height());

  // Change default cross-axis alignment.
  layout_->SetCrossAxisAlignment(LayoutAlignment::kCenter);
  test::RunScheduledLayout(host_.get());
  // v1-v4 should remain unchanged.
  EXPECT_EQ(5, v1->y());
  EXPECT_EQ(10, v1->height());
  EXPECT_EQ(1, v2->y());
  EXPECT_EQ(10, v2->height());
  EXPECT_EQ(-3, v3->y());
  EXPECT_EQ(10, v3->height());
  EXPECT_EQ(5, v4->y());
  EXPECT_EQ(2, v4->height());
  // Since v5 doesn't have its own alignment set, it should pick up the new
  // default.
  EXPECT_EQ(1, v5->y());
  EXPECT_EQ(10, v5->height());
}

TEST_F(FlexLayoutTest, IndividualCrossAxisAlignmentInVerticalLayoutTest) {
  layout_->SetOrientation(LayoutOrientation::kVertical)
      .SetDefault(kMarginsKey, gfx::Insets(5));
  View* const v1 = AddChild(gfx::Size(10, 10));
  v1->SetProperty(kCrossAxisAlignmentKey, LayoutAlignment::kStart);
  View* const v2 = AddChild(gfx::Size(10, 10));
  v2->SetProperty(kCrossAxisAlignmentKey, LayoutAlignment::kCenter);
  View* const v3 = AddChild(gfx::Size(10, 10));
  v3->SetProperty(kCrossAxisAlignmentKey, LayoutAlignment::kEnd);
  View* const v4 = AddChild(gfx::Size(10, 10));
  v4->SetProperty(kCrossAxisAlignmentKey, LayoutAlignment::kStretch);
  View* const v5 = AddChild(gfx::Size(10, 10));
  // v5 uses default

  host_->SizeToPreferredSize();
  EXPECT_EQ(5, v1->x());
  EXPECT_EQ(10, v1->width());
  EXPECT_EQ(5, v2->x());
  EXPECT_EQ(10, v2->width());
  EXPECT_EQ(5, v3->x());
  EXPECT_EQ(10, v3->width());
  EXPECT_EQ(5, v4->x());
  EXPECT_EQ(10, v4->width());
  EXPECT_EQ(5, v5->x());
  EXPECT_EQ(10, v5->width());

  // Next try a larger view.
  host_->SetSize(gfx::Size(30, 100));
  EXPECT_EQ(5, v1->x());
  EXPECT_EQ(10, v1->width());
  EXPECT_EQ(10, v2->x());
  EXPECT_EQ(10, v2->width());
  EXPECT_EQ(15, v3->x());
  EXPECT_EQ(10, v3->width());
  EXPECT_EQ(5, v4->x());
  EXPECT_EQ(20, v4->width());
  EXPECT_EQ(5, v5->x());
  EXPECT_EQ(20, v5->width());

  // Move to a smaller view.
  host_->SetSize(gfx::Size(12, 100));
  EXPECT_EQ(5, v1->x());
  EXPECT_EQ(10, v1->width());
  EXPECT_EQ(1, v2->x());
  EXPECT_EQ(10, v2->width());
  EXPECT_EQ(-3, v3->x());
  EXPECT_EQ(10, v3->width());
  EXPECT_EQ(5, v4->x());
  EXPECT_EQ(2, v4->width());
  EXPECT_EQ(5, v5->x());
  EXPECT_EQ(2, v5->width());

  // Change default cross-axis alignment.
  layout_->SetCrossAxisAlignment(LayoutAlignment::kCenter);
  test::RunScheduledLayout(host_.get());
  // v1-v4 should remain unchanged.
  EXPECT_EQ(5, v1->x());
  EXPECT_EQ(10, v1->width());
  EXPECT_EQ(1, v2->x());
  EXPECT_EQ(10, v2->width());
  EXPECT_EQ(-3, v3->x());
  EXPECT_EQ(10, v3->width());
  EXPECT_EQ(5, v4->x());
  EXPECT_EQ(2, v4->width());
  // Since v5 doesn't have its own alignment set, it should pick up the new
  // default.
  EXPECT_EQ(1, v5->x());
  EXPECT_EQ(10, v5->width());
}

TEST_F(FlexLayoutTest, PreferredSizeMutationTest) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal)
      .SetCrossAxisAlignment(views::LayoutAlignment::kStart)
      .SetIgnoreDefaultMainAxisMargins(true);

  // We want to specialize the maximum size and have different sizes within the
  // constraints of the scene
  const FlexSpecification custom_spec(
      base::BindRepeating([](const View* view, const SizeBounds& maximum_size) {
        // This custom rule looks strange, but it is constrained by the current
        // multi-line label using GetPreferredSize(const SizeBounds&
        // available_size). eg: HelpBubbleView. This is indeed the case. Here is
        // a simple simulation.
        return gfx::Size(maximum_size.width() >= 300
                             ? 300
                             : maximum_size.width().min_of(250),
                         24);
      }));

  View* const v1 = AddChild(gfx::Size(10, 24));
  v1->SetProperty(kFlexBehaviorKey, custom_spec.WithOrder(2));
  View* const v2 = AddChild(gfx::Size(24, 24));
  v2->SetProperty(kFlexBehaviorKey,
                  kUnbounded.WithAlignment(views::LayoutAlignment::kEnd));

  EXPECT_EQ(gfx::Size(324, 24), host_->GetPreferredSize({}));

  host_->SizeToPreferredSize();
  std::vector<Rect> expected = {{0, 0, 300, 24}, {300, 0, 24, 24}};
  EXPECT_EQ(expected, GetChildBounds());

  host_->SetSize({300, 24});
  expected = {{0, 0, 250, 24}, {276, 0, 24, 24}};
  EXPECT_EQ(expected, GetChildBounds());
}

TEST_F(FlexLayoutTest, PreferredSizeMutationTest2) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal)
      .SetCrossAxisAlignment(views::LayoutAlignment::kStart)
      .SetIgnoreDefaultMainAxisMargins(true);

  // We want to specialize the maximum size and have different sizes within the
  // constraints of the scene
  const FlexSpecification custom_spec(
      base::BindRepeating([](const View* view, const SizeBounds& maximum_size) {
        // This custom rule looks strange, but it is constrained by the current
        // multi-line label using GetPreferredSize(const SizeBounds&
        // available_size). eg: HelpBubbleView. This is indeed the case. Here is
        // a simple simulation.
        return gfx::Size(maximum_size.width() >= 300
                             ? 300
                             : maximum_size.width().min_of(250),
                         24);
      }));

  View* const v1 = AddChild(gfx::Size(10, 24));
  v1->SetProperty(kFlexBehaviorKey, custom_spec.WithOrder(2));
  View* const v2 = AddChild(gfx::Size(24, 24));
  v2->SetProperty(kFlexBehaviorKey,
                  kUnbounded.WithAlignment(views::LayoutAlignment::kCenter));
  View* const v3 = AddChild(gfx::Size(10, 24));
  v3->SetProperty(kFlexBehaviorKey, custom_spec.WithOrder(2));

  EXPECT_EQ(gfx::Size(624, 24), host_->GetPreferredSize({}));

  host_->SizeToPreferredSize();
  std::vector<Rect> expected = {
      {0, 0, 300, 24}, {300, 0, 24, 24}, {324, 0, 300, 24}};
  EXPECT_EQ(expected, GetChildBounds());

  // Test using critical value 300
  host_->SetSize({300, 24});
  expected = {{0, 0, 138, 24}, {138, 0, 24, 24}, {162, 0, 138, 24}};
  EXPECT_EQ(expected, GetChildBounds());

  // Test using critical value 524, It comes from the critical value 250+250+24
  // Values in [524, 622.5) should behave the same.
  //
  // Why is it 622.5? Because when the space less than 1.5 (300+300+24-1.5) is
  // evenly distributed, the available space of v1 will become 300 due to
  // rounding.
  host_->SetSize({524, 24});
  expected = {{0, 0, 250, 24}, {250, 0, 24, 24}, {274, 0, 250, 24}};
  EXPECT_EQ(expected, GetChildBounds());

  // Test using critical value 623
  host_->SetSize({623, 24});
  expected = {{0, 0, 300, 24}, {324, 0, 24, 24}, {373, 0, 250, 24}};
  EXPECT_EQ(expected, GetChildBounds());
}

TEST_F(FlexLayoutTest, ZeroPreferedSizeView) {
  layout_->SetOrientation(LayoutOrientation::kHorizontal)
      .SetCrossAxisAlignment(views::LayoutAlignment::kStart)
      .SetDefault(views::kMarginsKey, gfx::Insets::VH(0, 5))
      .SetIgnoreDefaultMainAxisMargins(true);

  View* const v1 = AddChild(gfx::Size(10, 24));
  View* const v2 = AddChild(gfx::Size(0, 24));
  v2->SetProperty(
      kFlexBehaviorKey,
      views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToMinimum,
                               views::MaximumFlexSizeRule::kScaleToMaximum));
  View* const v3 = AddChild(gfx::Size(24, 24));

  EXPECT_EQ(gfx::Size(44, 24), host_->GetPreferredSize({}));

  host_->SizeToPreferredSize();
  std::vector<Rect> expected = {{0, 0, 10, 24}, {0, 0, 0, 0}, {20, 0, 24, 24}};
  EXPECT_EQ(expected, GetChildBounds());
  EXPECT_TRUE(v1->GetVisible());
  EXPECT_FALSE(v2->GetVisible());
  EXPECT_TRUE(v3->GetVisible());
}

// Cross-axis Fit Tests --------------------------------------------------------

// Tests for cross-axis alignment that checks three different conditions:
//  - child1 fits entirely in the space provided, with margins
//  - child2 fits in the space, but its margins don't
//  - child3 does not fit in the space provided
class FlexLayoutCrossAxisFitTest : public FlexLayoutTest {
 public:
  void SetUp() override {
    FlexLayoutTest::SetUp();
    DCHECK(child_views_.empty());

    for (size_t i = 0; i < kNumChildren; ++i) {
      View* const child = AddChild(kChildSizes[i]);
      child->SetProperty(kMarginsKey, gfx::Insets(kChildMargins[i]));
      child_views_.push_back(child);
    }

    layout_->SetOrientation(LayoutOrientation::kHorizontal);
    layout_->SetCollapseMargins(true);
    layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
    host_->SetSize(kHostSize);
  }

  void TearDown() override { child_views_.clear(); }

 protected:
  static constexpr size_t kNumChildren = 3;
  static constexpr gfx::Size kHostSize = gfx::Size(200, 20);
  static constexpr std::array<gfx::Size, kNumChildren> kChildSizes = {
      {{10, 10}, {10, 10}, {10, 30}}};
  static constexpr std::array<gfx::Insets, kNumChildren> kChildMargins = {
      {gfx::Insets::TLBR(6, 0, 2, 0), gfx::Insets::TLBR(10, 0, 5, 0),
       gfx::Insets::TLBR(6, 0, 2, 0)}};

  std::vector<raw_ptr<View, VectorExperimental>> child_views_;
};

TEST_F(FlexLayoutCrossAxisFitTest, Layout_CrossStretch) {
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStretch);
  test::RunScheduledLayout(host_.get());

  // Expect child views to respect their leading margin and to occupy all
  // available space (other than margins), with a minimum size of zero.
  for (size_t i = 0; i < kNumChildren; ++i) {
    EXPECT_EQ(kChildMargins[i].top(), child_views_[i]->origin().y());
    const int expected_height =
        std::max(0, kHostSize.height() - kChildMargins[i].height());
    EXPECT_EQ(expected_height, child_views_[i]->size().height());
  }
}

TEST_F(FlexLayoutCrossAxisFitTest, Layout_CrossStart) {
  layout_->SetCrossAxisAlignment(LayoutAlignment::kStart);
  test::RunScheduledLayout(host_.get());

  // These should all justify to the leading edge and keep their original size.
  for (size_t i = 0; i < kNumChildren; ++i) {
    EXPECT_EQ(kChildMargins[i].top(), child_views_[i]->origin().y());
    EXPECT_EQ(kChildSizes[i].height(), child_views_[i]->size().height());
  }
}

TEST_F(FlexLayoutCrossAxisFitTest, Layout_CrossCenter) {
  layout_->SetCrossAxisAlignment(LayoutAlignment::kCenter);
  test::RunScheduledLayout(host_.get());

  // First child view fits entirely in the host view with margins (18 DIPs).
  // The entire height (including margins) will be centered vertically.
  int remain = kHostSize.height() -
               (kChildSizes[0].height() + kChildMargins[0].height());
  int expected = remain / 2 + kChildMargins[0].top();
  EXPECT_EQ(expected, child_views_[0]->origin().y());

  // Second child view is smaller than the host view, but margins don't fit.
  // The margins will be scaled down.
  remain = kHostSize.height() - kChildSizes[0].height();
  expected =
      base::ClampRound(kChildMargins[1].top() * static_cast<float>(remain) /
                       kChildMargins[1].height());
  EXPECT_EQ(expected, child_views_[1]->origin().y());

  // Third child view does not fit, so is centered.
  remain = kHostSize.height() - kChildSizes[2].height();
  expected = std::ceilf(remain * 0.5f);
  EXPECT_EQ(expected, child_views_[2]->origin().y());

  // Expect child views to retain their preferred sizes.
  for (size_t i = 0; i < kNumChildren; ++i) {
    EXPECT_EQ(kChildSizes[i].height(), child_views_[i]->size().height());
  }
}

TEST_F(FlexLayoutCrossAxisFitTest, Layout_CrossEnd) {
  layout_->SetCrossAxisAlignment(LayoutAlignment::kEnd);
  test::RunScheduledLayout(host_.get());

  // These should all justify to the trailing edge and keep their original size.
  for (size_t i = 0; i < kNumChildren; ++i) {
    EXPECT_EQ(kHostSize.height() - kChildMargins[i].bottom(),
              child_views_[i]->bounds().bottom());
    EXPECT_EQ(kChildSizes[i].height(), child_views_[i]->size().height());
  }
}

// Nested Layout Tests ---------------------------------------------------------

class NestedFlexLayoutTest : public FlexLayoutTest {
 public:
  void AddChildren(size_t num_children) {
    for (size_t i = 0; i < num_children; ++i) {
      auto v = std::make_unique<View>();
      FlexLayout* layout = v->SetLayoutManager(std::make_unique<FlexLayout>());
      children_.push_back(v.get());
      layouts_.push_back(layout);
      host_->AddChildView(std::move(v));
    }
  }

  View* AddGrandchild(size_t child_index,
                      const gfx::Size& preferred,
                      const std::optional<gfx::Size>& minimum = std::nullopt) {
    return AddChild(children_[child_index - 1], preferred, minimum);
  }

  View* child(size_t child_index) const { return children_[child_index - 1]; }

  FlexLayout* layout(size_t child_index) const {
    return layouts_[child_index - 1];
  }

  View* grandchild(size_t child_index, size_t grandchild_index) const {
    return children_[child_index - 1]->children()[grandchild_index - 1];
  }

 private:
  std::vector<raw_ptr<FlexLayout, VectorExperimental>> layouts_;
  View::Views children_;
};

TEST_F(NestedFlexLayoutTest, SetVisible_UpdatesLayout) {
  AddChildren(1);
  AddGrandchild(1, gfx::Size(5, 5));
  AddGrandchild(1, gfx::Size(5, 5));

  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout(1)->SetOrientation(LayoutOrientation::kHorizontal);
  EXPECT_EQ(gfx::Size(10, 5), host_->GetPreferredSize({}));
  grandchild(1, 1)->SetVisible(false);
  EXPECT_EQ(gfx::Size(5, 5), host_->GetPreferredSize({}));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(gfx::Rect(0, 0, 5, 5), child(1)->bounds());
  EXPECT_EQ(gfx::Rect(0, 0, 5, 5), grandchild(1, 2)->bounds());
}

TEST_F(NestedFlexLayoutTest, AddChild_UpdatesLayout) {
  AddChildren(1);
  AddGrandchild(1, gfx::Size(5, 5));

  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout(1)->SetOrientation(LayoutOrientation::kHorizontal);
  EXPECT_EQ(gfx::Size(5, 5), host_->GetPreferredSize({}));
  AddGrandchild(1, gfx::Size(5, 5));
  EXPECT_EQ(gfx::Size(10, 5), host_->GetPreferredSize({}));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(gfx::Rect(0, 0, 10, 5), child(1)->bounds());
  EXPECT_EQ(gfx::Rect(0, 0, 5, 5), grandchild(1, 1)->bounds());
  EXPECT_EQ(gfx::Rect(5, 0, 5, 5), grandchild(1, 2)->bounds());
}

TEST_F(NestedFlexLayoutTest, RemoveChild_UpdatesLayout) {
  AddChildren(1);
  AddGrandchild(1, gfx::Size(5, 5));
  AddGrandchild(1, gfx::Size(5, 5));

  layout_->SetOrientation(LayoutOrientation::kHorizontal);
  layout(1)->SetOrientation(LayoutOrientation::kHorizontal);
  EXPECT_EQ(gfx::Size(10, 5), host_->GetPreferredSize({}));

  // Remove one grandchild view, avoiding a memory leak since the view is no
  // longer owned.
  View* const to_remove = grandchild(1, 2);
  child(1)->RemoveChildView(to_remove);
  delete to_remove;

  EXPECT_EQ(gfx::Size(5, 5), host_->GetPreferredSize({}));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(gfx::Rect(0, 0, 5, 5), child(1)->bounds());
  EXPECT_EQ(gfx::Rect(0, 0, 5, 5), grandchild(1, 1)->bounds());
}

TEST_F(NestedFlexLayoutTest, Layout_OppositeOrientation) {
  AddChildren(2);
  AddGrandchild(1, gfx::Size(5, 5));
  AddGrandchild(1, gfx::Size(5, 5));
  AddGrandchild(2, gfx::Size(6, 6));
  AddGrandchild(2, gfx::Size(6, 6));

  layout_->SetOrientation(LayoutOrientation::kHorizontal)
      .SetCollapseMargins(false)
      .SetCrossAxisAlignment(LayoutAlignment::kStart)
      .SetDefault(views::kMarginsKey, gfx::Insets::TLBR(2, 3, 4, 5))
      .SetInteriorMargin(gfx::Insets::TLBR(4, 3, 2, 1));

  layout(1)
      ->SetOrientation(LayoutOrientation::kVertical)
      .SetCollapseMargins(true)
      .SetDefault(views::kMarginsKey, gfx::Insets(2))
      .SetInteriorMargin(gfx::Insets(1));

  layout(2)
      ->SetOrientation(LayoutOrientation::kVertical)
      .SetCollapseMargins(true)
      .SetDefault(views::kMarginsKey, gfx::Insets(1))
      .SetInteriorMargin(gfx::Insets(2));

  EXPECT_EQ(gfx::Size(39, 29), host_->GetPreferredSize({}));
  host_->SetSize(gfx::Size(50, 30));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(gfx::Rect(6, 6, 9, 16), child(1)->bounds());
  EXPECT_EQ(gfx::Rect(23, 6, 10, 17), child(2)->bounds());
  EXPECT_EQ(gfx::Rect(2, 2, 5, 5), grandchild(1, 1)->bounds());
  EXPECT_EQ(gfx::Rect(2, 9, 5, 5), grandchild(1, 2)->bounds());
  EXPECT_EQ(gfx::Rect(2, 2, 6, 6), grandchild(2, 1)->bounds());
  EXPECT_EQ(gfx::Rect(2, 9, 6, 6), grandchild(2, 2)->bounds());
}

TEST_F(NestedFlexLayoutTest, Layout_SameOrientation) {
  AddChildren(2);
  AddGrandchild(1, gfx::Size(5, 5));
  AddGrandchild(1, gfx::Size(5, 5));
  AddGrandchild(2, gfx::Size(6, 6));
  AddGrandchild(2, gfx::Size(6, 6));

  layout_->SetOrientation(LayoutOrientation::kHorizontal)
      .SetCollapseMargins(false)
      .SetCrossAxisAlignment(LayoutAlignment::kStart)
      .SetDefault(views::kMarginsKey, gfx::Insets::TLBR(2, 3, 4, 5))
      .SetInteriorMargin(gfx::Insets::TLBR(4, 3, 2, 1));

  layout(1)
      ->SetOrientation(LayoutOrientation::kHorizontal)
      .SetCollapseMargins(true)
      .SetDefault(views::kMarginsKey, gfx::Insets(2))
      .SetInteriorMargin(gfx::Insets(1));

  layout(2)
      ->SetOrientation(LayoutOrientation::kHorizontal)
      .SetCollapseMargins(true)
      .SetDefault(views::kMarginsKey, gfx::Insets(1))
      .SetInteriorMargin(gfx::Insets(2));

  EXPECT_EQ(gfx::Size(53, 22), host_->GetPreferredSize({}));
  host_->SetSize(gfx::Size(60, 30));
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(gfx::Rect(6, 6, 16, 9), child(1)->bounds());
  EXPECT_EQ(gfx::Rect(30, 6, 17, 10), child(2)->bounds());
  EXPECT_EQ(gfx::Rect(2, 2, 5, 5), grandchild(1, 1)->bounds());
  EXPECT_EQ(gfx::Rect(9, 2, 5, 5), grandchild(1, 2)->bounds());
  EXPECT_EQ(gfx::Rect(2, 2, 6, 6), grandchild(2, 1)->bounds());
  EXPECT_EQ(gfx::Rect(9, 2, 6, 6), grandchild(2, 2)->bounds());
}

TEST_F(NestedFlexLayoutTest, Layout_Flex) {
  AddChildren(2);
  AddGrandchild(1, gfx::Size(5, 5));
  AddGrandchild(1, gfx::Size(5, 5));
  AddGrandchild(2, gfx::Size(6, 6));
  AddGrandchild(2, gfx::Size(6, 6));

  layout_->SetOrientation(LayoutOrientation::kHorizontal)
      .SetCollapseMargins(true)
      .SetCrossAxisAlignment(LayoutAlignment::kStart)
      .SetDefault(views::kMarginsKey, gfx::Insets(2))
      .SetInteriorMargin(gfx::Insets(2));
  child(1)->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);
  child(2)->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);

  layout(1)
      ->SetOrientation(LayoutOrientation::kHorizontal)
      .SetCollapseMargins(true)
      .SetDefault(views::kMarginsKey, gfx::Insets(2))
      .SetInteriorMargin(gfx::Insets(2));
  grandchild(1, 1)->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);
  grandchild(1, 2)->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);

  layout(2)
      ->SetOrientation(LayoutOrientation::kHorizontal)
      .SetCollapseMargins(true)
      .SetDefault(views::kMarginsKey, gfx::Insets(2))
      .SetInteriorMargin(gfx::Insets(2));
  grandchild(2, 1)->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);
  grandchild(2, 2)->SetProperty(views::kFlexBehaviorKey, kFlex1ScaleToZero);

  EXPECT_EQ(gfx::Size(40, 14), host_->GetPreferredSize({}));

  host_->SetSize(gfx::Size(26, 15));
  EXPECT_EQ(gfx::Rect(2, 2, 9, 9), child(1)->bounds());
  EXPECT_EQ(gfx::Rect(13, 2, 11, 10), child(2)->bounds());
  EXPECT_EQ(gfx::Rect(2, 2, 2, 5), grandchild(1, 1)->bounds());
  EXPECT_EQ(gfx::Rect(6, 2, 1, 5), grandchild(1, 2)->bounds());
  EXPECT_EQ(gfx::Rect(2, 2, 3, 6), grandchild(2, 1)->bounds());
  EXPECT_EQ(gfx::Rect(7, 2, 2, 6), grandchild(2, 2)->bounds());

  host_->SetSize(gfx::Size(22, 15));
  EXPECT_EQ(gfx::Rect(2, 2, 7, 9), child(1)->bounds());
  EXPECT_EQ(gfx::Rect(11, 2, 9, 10), child(2)->bounds());
  EXPECT_TRUE(grandchild(1, 1)->GetVisible());
  EXPECT_FALSE(grandchild(1, 2)->GetVisible());
  EXPECT_EQ(gfx::Rect(2, 2, 3, 5), grandchild(1, 1)->bounds());
  EXPECT_EQ(gfx::Rect(2, 2, 2, 6), grandchild(2, 1)->bounds());
  EXPECT_EQ(gfx::Rect(6, 2, 1, 6), grandchild(2, 2)->bounds());

  host_->SetSize(gfx::Size(20, 15));
  EXPECT_EQ(gfx::Rect(2, 2, 6, 9), child(1)->bounds());
  EXPECT_EQ(gfx::Rect(10, 2, 8, 10), child(2)->bounds());
  EXPECT_TRUE(grandchild(1, 1)->GetVisible());
  EXPECT_FALSE(grandchild(1, 2)->GetVisible());
  EXPECT_EQ(gfx::Rect(2, 2, 2, 5), grandchild(1, 1)->bounds());
  EXPECT_EQ(gfx::Rect(2, 2, 1, 6), grandchild(2, 1)->bounds());
  EXPECT_EQ(gfx::Rect(5, 2, 1, 6), grandchild(2, 2)->bounds());
}

TEST_F(NestedFlexLayoutTest, UsingDefaultFlexRule) {
  AddChildren(2);
  AddGrandchild(1, gfx::Size(5, 5));
  AddGrandchild(2, gfx::Size(5, 5));
  AddGrandchild(2, gfx::Size(5, 5));

  child(1)->SetProperty(
      kFlexBehaviorKey,
      FlexSpecification(layout(1)->GetDefaultFlexRule()).WithOrder(2));
  child(2)->SetProperty(kFlexBehaviorKey,
                        FlexSpecification(layout(2)->GetDefaultFlexRule()));
  grandchild(1, 1)->SetProperty(kFlexBehaviorKey, kUnboundedScaleToZero);
  grandchild(2, 1)->SetProperty(kFlexBehaviorKey, kDropOut);
  grandchild(2, 2)->SetProperty(kFlexBehaviorKey, kDropOut);

  // Extra flex space is allocated to the first child view.
  host_->SetSize(gfx::Size(17, 5));
  EXPECT_TRUE(child(1)->GetVisible());
  EXPECT_EQ(gfx::Size(7, 5), child(1)->size());
  EXPECT_TRUE(grandchild(1, 1)->GetVisible());
  EXPECT_EQ(gfx::Size(7, 5), grandchild(1, 1)->size());
  EXPECT_TRUE(child(2)->GetVisible());
  EXPECT_EQ(gfx::Size(10, 5), child(2)->size());
  EXPECT_TRUE(grandchild(2, 1)->GetVisible());
  EXPECT_EQ(gfx::Size(5, 5), grandchild(2, 1)->size());
  EXPECT_TRUE(grandchild(2, 2)->GetVisible());
  EXPECT_EQ(gfx::Size(5, 5), grandchild(2, 2)->size());

  // Leftover flex space is still allocated to the first child view even after
  // one of the grandchildren drops out.
  host_->SetSize(gfx::Size(8, 5));
  EXPECT_TRUE(child(1)->GetVisible());
  EXPECT_EQ(gfx::Size(3, 5), child(1)->size());
  EXPECT_TRUE(grandchild(1, 1)->GetVisible());
  EXPECT_EQ(gfx::Size(3, 5), grandchild(1, 1)->size());
  EXPECT_TRUE(child(2)->GetVisible());
  EXPECT_EQ(gfx::Size(5, 5), child(2)->size());
  EXPECT_TRUE(grandchild(2, 1)->GetVisible());
  EXPECT_EQ(gfx::Size(5, 5), grandchild(2, 1)->size());
  EXPECT_FALSE(grandchild(2, 2)->GetVisible());

  // Leftover flex space is still allocated to the first child view even after
  // two of the grandchildren drop out.
  host_->SetSize(gfx::Size(4, 5));
  EXPECT_TRUE(child(1)->GetVisible());
  EXPECT_EQ(gfx::Size(4, 5), child(1)->size());
  EXPECT_TRUE(grandchild(1, 1)->GetVisible());
  EXPECT_EQ(gfx::Size(4, 5), grandchild(1, 1)->size());
  EXPECT_FALSE(child(2)->GetVisible()) << child(2)->bounds().ToString();

  // If there is no leftover space, the first child view is hidden.
  host_->SetSize(gfx::Size(5, 5));
  EXPECT_FALSE(child(1)->GetVisible());
  EXPECT_TRUE(child(2)->GetVisible());
  EXPECT_EQ(gfx::Size(5, 5), child(2)->size());
  EXPECT_TRUE(grandchild(2, 1)->GetVisible());
  EXPECT_EQ(gfx::Size(5, 5), grandchild(2, 1)->size());
  EXPECT_FALSE(grandchild(2, 2)->GetVisible());
}

TEST_F(NestedFlexLayoutTest, UnboundedZeroSize) {
  AddChildren(1);
  child(1)->SetProperty(views::kFlexBehaviorKey, kUnboundedScaleToZero);

  AddGrandchild(1, gfx::Size(0, 5));
  grandchild(1, 1)->SetProperty(views::kFlexBehaviorKey, kUnboundedScaleToZero);

  EXPECT_EQ(5, child(1)->GetPreferredSize({}).height());
  host_->SetSize(gfx::Size(100, 5));
  EXPECT_EQ(5, child(1)->GetPreferredSize({}).height());
  test::RunScheduledLayout(host_.get());
  EXPECT_EQ(5, child(1)->height());
}

namespace {

struct DirectionalFlexRuleTestParamRules {
  const char* const name;
  MinimumFlexSizeRule min_main_rule;
  MaximumFlexSizeRule max_main_rule;
  bool use_height_for_width = false;
  MinimumFlexSizeRule min_cross_rule = MinimumFlexSizeRule::kPreferred;
};

struct DirectionalFlexRuleTestParam {
  LayoutOrientation orientation;
  DirectionalFlexRuleTestParamRules rules;
  gfx::Size size;
  std::vector<gfx::Rect> expected;

  std::string ToString() const {
    std::ostringstream oss;
    PrintTo(orientation, &oss);
    oss << " " << rules.name << " " << size.ToString();
    return oss.str();
  }
};

// No flex in cross-axis direction.
static const DirectionalFlexRuleTestParamRules kNoCrossFlex = {
    "Main unbounded scale to minimum snap to zero, cross no flex.",
    MinimumFlexSizeRule::kScaleToMinimumSnapToZero,
    MaximumFlexSizeRule::kUnbounded};

// Drop out on main axis, flex on cross axis.
static const DirectionalFlexRuleTestParamRules kMainDropOutCrossFlex = {
    "Main preferred snap to zero, cross unbounded scale to zero.",
    MinimumFlexSizeRule::kPreferredSnapToZero, MaximumFlexSizeRule::kPreferred,
    false, MinimumFlexSizeRule::kScaleToZero};

// Preferred height-for-width on main axis, scale to minimum snap to zero on
// cross axis. (Note: Vertical only!)
static const DirectionalFlexRuleTestParamRules kFlexUseHeightForWidth = {
    "Cross scale to minimum snap to zero, main use height for width.",
    MinimumFlexSizeRule::kPreferred, MaximumFlexSizeRule::kPreferred, true,
    MinimumFlexSizeRule::kScaleToMinimumSnapToZero};

const DirectionalFlexRuleTestParam DirectionalFlexRuleTestParamList[] = {
    {LayoutOrientation::kHorizontal,
     kNoCrossFlex,
     gfx::Size(20, 10),
     {{0, 0, 10, 10}, {10, 0, 10, 10}}},
    {LayoutOrientation::kHorizontal,
     kNoCrossFlex,
     gfx::Size(30, 10),
     {{0, 0, 10, 10}, {10, 0, 20, 10}}},
    {LayoutOrientation::kHorizontal,
     kNoCrossFlex,
     gfx::Size(16, 10),
     {{0, 0, 10, 10}, {10, 0, 6, 10}}},
    {LayoutOrientation::kHorizontal,
     kNoCrossFlex,
     gfx::Size(14, 10),
     {{0, 0, 10, 10}, {}}},
    {LayoutOrientation::kHorizontal,
     kNoCrossFlex,
     gfx::Size(20, 20),
     {{0, 5, 10, 10}, {10, 5, 10, 10}}},
    {LayoutOrientation::kHorizontal,
     kNoCrossFlex,
     gfx::Size(30, 20),
     {{0, 5, 10, 10}, {10, 5, 20, 10}}},
    {LayoutOrientation::kHorizontal,
     kNoCrossFlex,
     gfx::Size(16, 20),
     {{0, 5, 10, 10}, {10, 5, 6, 10}}},
    {LayoutOrientation::kHorizontal,
     kNoCrossFlex,
     gfx::Size(14, 20),
     {{0, 5, 10, 10}, {}}},
    {LayoutOrientation::kHorizontal,
     kNoCrossFlex,
     gfx::Size(20, 6),
     {{0, -2, 10, 10}, {10, -2, 10, 10}}},
    {LayoutOrientation::kHorizontal,
     kNoCrossFlex,
     gfx::Size(30, 6),
     {{0, -2, 10, 10}, {10, -2, 20, 10}}},
    {LayoutOrientation::kHorizontal,
     kNoCrossFlex,
     gfx::Size(16, 6),
     {{0, -2, 10, 10}, {10, -2, 6, 10}}},
    {LayoutOrientation::kHorizontal,
     kNoCrossFlex,
     gfx::Size(14, 6),
     {{0, -2, 10, 10}, {}}},
    {LayoutOrientation::kHorizontal,
     kNoCrossFlex,
     gfx::Size(20, 4),
     {{0, -3, 10, 10}, {10, -3, 10, 10}}},
    {LayoutOrientation::kHorizontal,
     kNoCrossFlex,
     gfx::Size(30, 4),
     {{0, -3, 10, 10}, {10, -3, 20, 10}}},
    {LayoutOrientation::kHorizontal,
     kNoCrossFlex,
     gfx::Size(16, 4),
     {{0, -3, 10, 10}, {10, -3, 6, 10}}},
    {LayoutOrientation::kHorizontal,
     kNoCrossFlex,
     gfx::Size(14, 4),
     {{0, -3, 10, 10}, {}}},
    {LayoutOrientation::kHorizontal,
     kMainDropOutCrossFlex,
     gfx::Size(20, 10),
     {{0, 0, 10, 10}, {10, 0, 10, 10}}},
    {LayoutOrientation::kHorizontal,
     kMainDropOutCrossFlex,
     gfx::Size(30, 10),
     {{0, 0, 10, 10}, {10, 0, 10, 10}}},
    {LayoutOrientation::kHorizontal,
     kMainDropOutCrossFlex,
     gfx::Size(16, 10),
     {{0, 0, 10, 10}, {}}},
    {LayoutOrientation::kHorizontal,
     kMainDropOutCrossFlex,
     gfx::Size(14, 10),
     {{0, 0, 10, 10}, {}}},
    {LayoutOrientation::kHorizontal,
     kMainDropOutCrossFlex,
     gfx::Size(20, 20),
     {{0, 5, 10, 10}, {10, 5, 10, 10}}},
    {LayoutOrientation::kHorizontal,
     kMainDropOutCrossFlex,
     gfx::Size(30, 20),
     {{0, 5, 10, 10}, {10, 5, 10, 10}}},
    {LayoutOrientation::kHorizontal,
     kMainDropOutCrossFlex,
     gfx::Size(16, 20),
     {{0, 5, 10, 10}, {}}},
    {LayoutOrientation::kHorizontal,
     kMainDropOutCrossFlex,
     gfx::Size(14, 20),
     {{0, 5, 10, 10}, {}}},
    {LayoutOrientation::kHorizontal,
     kMainDropOutCrossFlex,
     gfx::Size(20, 6),
     {{0, -2, 10, 10}, {10, 0, 10, 6}}},
    {LayoutOrientation::kHorizontal,
     kMainDropOutCrossFlex,
     gfx::Size(30, 6),
     {{0, -2, 10, 10}, {10, 0, 10, 6}}},
    {LayoutOrientation::kHorizontal,
     kMainDropOutCrossFlex,
     gfx::Size(16, 6),
     {{0, -2, 10, 10}, {}}},
    {LayoutOrientation::kHorizontal,
     kMainDropOutCrossFlex,
     gfx::Size(14, 6),
     {{0, -2, 10, 10}, {}}},
    {LayoutOrientation::kHorizontal,
     kMainDropOutCrossFlex,
     gfx::Size(20, 4),
     {{0, -3, 10, 10}, {10, 0, 10, 4}}},
    {LayoutOrientation::kHorizontal,
     kMainDropOutCrossFlex,
     gfx::Size(30, 4),
     {{0, -3, 10, 10}, {10, 0, 10, 4}}},
    {LayoutOrientation::kHorizontal,
     kMainDropOutCrossFlex,
     gfx::Size(16, 4),
     {{0, -3, 10, 10}, {}}},
    {LayoutOrientation::kHorizontal,
     kMainDropOutCrossFlex,
     gfx::Size(14, 4),
     {{0, -3, 10, 10}, {}}},
    {LayoutOrientation::kVertical,
     kNoCrossFlex,
     gfx::Size(10, 20),
     {{0, 0, 10, 10}, {0, 10, 10, 10}}},
    {LayoutOrientation::kVertical,
     kNoCrossFlex,
     gfx::Size(10, 30),
     {{0, 0, 10, 10}, {0, 10, 10, 20}}},
    {LayoutOrientation::kVertical,
     kNoCrossFlex,
     gfx::Size(10, 16),
     {{0, 0, 10, 10}, {0, 10, 10, 6}}},
    {LayoutOrientation::kVertical,
     kNoCrossFlex,
     gfx::Size(10, 14),
     {{0, 0, 10, 10}, {}}},
    {LayoutOrientation::kVertical,
     kNoCrossFlex,
     gfx::Size(20, 20),
     {{5, 0, 10, 10}, {5, 10, 10, 10}}},
    {LayoutOrientation::kVertical,
     kNoCrossFlex,
     gfx::Size(20, 30),
     {{5, 0, 10, 10}, {5, 10, 10, 20}}},
    {LayoutOrientation::kVertical,
     kNoCrossFlex,
     gfx::Size(20, 16),
     {{5, 0, 10, 10}, {5, 10, 10, 6}}},
    {LayoutOrientation::kVertical,
     kNoCrossFlex,
     gfx::Size(20, 14),
     {{5, 0, 10, 10}, {}}},
    {LayoutOrientation::kVertical,
     kNoCrossFlex,
     gfx::Size(6, 20),
     {{-2, 0, 10, 10}, {-2, 10, 10, 10}}},
    {LayoutOrientation::kVertical,
     kNoCrossFlex,
     gfx::Size(6, 30),
     {{-2, 0, 10, 10}, {-2, 10, 10, 20}}},
    {LayoutOrientation::kVertical,
     kNoCrossFlex,
     gfx::Size(6, 16),
     {{-2, 0, 10, 10}, {-2, 10, 10, 6}}},
    {LayoutOrientation::kVertical,
     kNoCrossFlex,
     gfx::Size(6, 14),
     {{-2, 0, 10, 10}, {}}},
    {LayoutOrientation::kVertical,
     kNoCrossFlex,
     gfx::Size(4, 20),
     {{-3, 0, 10, 10}, {-3, 10, 10, 10}}},
    {LayoutOrientation::kVertical,
     kNoCrossFlex,
     gfx::Size(4, 30),
     {{-3, 0, 10, 10}, {-3, 10, 10, 20}}},
    {LayoutOrientation::kVertical,
     kNoCrossFlex,
     gfx::Size(4, 16),
     {{-3, 0, 10, 10}, {-3, 10, 10, 6}}},
    {LayoutOrientation::kVertical,
     kNoCrossFlex,
     gfx::Size(4, 14),
     {{-3, 0, 10, 10}, {}}},
    {LayoutOrientation::kVertical,
     kMainDropOutCrossFlex,
     gfx::Size(10, 20),
     {{0, 0, 10, 10}, {0, 10, 10, 10}}},
    {LayoutOrientation::kVertical,
     kMainDropOutCrossFlex,
     gfx::Size(10, 30),
     {{0, 0, 10, 10}, {0, 10, 10, 10}}},
    {LayoutOrientation::kVertical,
     kMainDropOutCrossFlex,
     gfx::Size(10, 16),
     {{0, 0, 10, 10}, {}}},
    {LayoutOrientation::kVertical,
     kMainDropOutCrossFlex,
     gfx::Size(10, 14),
     {{0, 0, 10, 10}, {}}},
    {LayoutOrientation::kVertical,
     kMainDropOutCrossFlex,
     gfx::Size(20, 20),
     {{5, 0, 10, 10}, {5, 10, 10, 10}}},
    {LayoutOrientation::kVertical,
     kMainDropOutCrossFlex,
     gfx::Size(20, 30),
     {{5, 0, 10, 10}, {5, 10, 10, 10}}},
    {LayoutOrientation::kVertical,
     kMainDropOutCrossFlex,
     gfx::Size(20, 16),
     {{5, 0, 10, 10}, {}}},
    {LayoutOrientation::kVertical,
     kMainDropOutCrossFlex,
     gfx::Size(20, 14),
     {{5, 0, 10, 10}, {}}},
    {LayoutOrientation::kVertical,
     kMainDropOutCrossFlex,
     gfx::Size(6, 20),
     {{-2, 0, 10, 10}, {0, 10, 6, 10}}},
    {LayoutOrientation::kVertical,
     kMainDropOutCrossFlex,
     gfx::Size(6, 30),
     {{-2, 0, 10, 10}, {0, 10, 6, 10}}},
    {LayoutOrientation::kVertical,
     kMainDropOutCrossFlex,
     gfx::Size(6, 16),
     {{-2, 0, 10, 10}, {}}},
    {LayoutOrientation::kVertical,
     kMainDropOutCrossFlex,
     gfx::Size(6, 14),
     {{-2, 0, 10, 10}, {}}},
    {LayoutOrientation::kVertical,
     kMainDropOutCrossFlex,
     gfx::Size(4, 20),
     {{-3, 0, 10, 10}, {0, 10, 4, 10}}},
    {LayoutOrientation::kVertical,
     kMainDropOutCrossFlex,
     gfx::Size(4, 30),
     {{-3, 0, 10, 10}, {0, 10, 4, 10}}},
    {LayoutOrientation::kVertical,
     kMainDropOutCrossFlex,
     gfx::Size(4, 16),
     {{-3, 0, 10, 10}, {}}},
    {LayoutOrientation::kVertical,
     kMainDropOutCrossFlex,
     gfx::Size(4, 14),
     {{-3, 0, 10, 10}, {}}},
    {LayoutOrientation::kVertical,
     kFlexUseHeightForWidth,
     gfx::Size(10, 20),
     {{0, 0, 10, 10}, {0, 10, 10, 10}}},
    {LayoutOrientation::kVertical,
     kFlexUseHeightForWidth,
     gfx::Size(20, 20),
     {{5, 0, 10, 10}, {5, 10, 10, 10}}},
    {LayoutOrientation::kVertical,
     kFlexUseHeightForWidth,
     gfx::Size(10, 30),
     {{0, 0, 10, 10}, {0, 10, 10, 10}}},
    {LayoutOrientation::kVertical,
     kFlexUseHeightForWidth,
     gfx::Size(8, 30),
     {{-1, 0, 10, 10}, {0, 10, 8, 12}}},
    {LayoutOrientation::kVertical,
     kFlexUseHeightForWidth,
     gfx::Size(6, 20),
     {{-2, 0, 10, 10}, {0, 10, 6, 16}}},
    {LayoutOrientation::kVertical,
     kFlexUseHeightForWidth,
     gfx::Size(4, 20),
     {{-3, 0, 10, 10}, {2, 10, 0, 20}}},
};

}  // anonymous namespace

class FlexLayoutDirectionalRuleTest
    : public FlexLayoutTest,
      public testing::WithParamInterface<DirectionalFlexRuleTestParam> {};

TEST_P(FlexLayoutDirectionalRuleTest, TestRules) {
  const DirectionalFlexRuleTestParam& param = GetParam();
  constexpr gfx::Size kChildSize(10, 10);
  constexpr gfx::Size kMinimumSize(5, 5);
  layout_->SetOrientation(param.orientation);
  layout_->SetMainAxisAlignment(LayoutAlignment::kStart);
  layout_->SetCrossAxisAlignment(LayoutAlignment::kCenter);
  AddChild(kChildSize);
  MockView* const child2 = AddChild(kChildSize, kMinimumSize);
  child2->SetProperty(
      kFlexBehaviorKey,
      FlexSpecification(param.orientation, param.rules.min_main_rule,
                        param.rules.max_main_rule,
                        param.rules.use_height_for_width,
                        param.rules.min_cross_rule));
  if (param.rules.use_height_for_width) {
    child2->set_size_mode(MockView::SizeMode::kFixedArea);
  }

  host_->SetSize(param.size);
  EXPECT_EQ(param.expected, GetChildBounds())
      << " Params: " << param.ToString();
}

INSTANTIATE_TEST_SUITE_P(,
                         FlexLayoutDirectionalRuleTest,
                         testing::ValuesIn(DirectionalFlexRuleTestParamList));

}  // namespace views