chromium/ui/views/layout/flex_layout_unittest.cc

// 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 "base/ranges/algorithm.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 {

Insets;
Point;
Rect;
Size;
optional;

class MockView : public View {};

BEGIN_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) {}

class FlexLayoutTest : public testing::Test {};

// 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 =;
const FlexSpecification FlexLayoutTest::kDropOut =;
const FlexSpecification FlexLayoutTest::kDropOutHighPriority =;
const FlexSpecification FlexLayoutTest::kFlex1ScaleToZero =;
const FlexSpecification FlexLayoutTest::kFlex1ScaleToMinimum =;
const FlexSpecification FlexLayoutTest::kFlex2ScaleToMinimum =;
const FlexSpecification FlexLayoutTest::kFlex1ScaleToMinimumHighPriority =;
const FlexSpecification FlexLayoutTest::kUnbounded =;
const FlexSpecification FlexLayoutTest::kUnboundedSnapToMinimum =;
const FlexSpecification FlexLayoutTest::kUnboundedSnapToZero =;
const FlexSpecification FlexLayoutTest::kUnboundedScaleToMinimumSnapToZero =;
const FlexSpecification FlexLayoutTest::kUnboundedScaleToZero =;
const FlexSpecification FlexLayoutTest::kUnboundedScaleToZeroAdjustHeight =;
const FlexSpecification FlexLayoutTest::kUnboundedScaleToMinimumHighPriority(
    MinimumFlexSizeRule::kScaleToMinimum,
    MaximumFlexSizeRule::kUnbounded);
const FlexSpecification FlexLayoutTest::kUnboundedScaleToMinimum =;

const FlexSpecification FlexLayoutTest::kUnboundedSnapToMinimumHorizontal =;
const FlexSpecification
    FlexLayoutTest::kUnboundedScaleToMinimumSnapToZeroHorizontal =;
const FlexSpecification FlexLayoutTest::kUnboundedScaleToZeroHorizontal =;

const FlexSpecification FlexLayoutTest::kScaleToMaximum =;

const FlexSpecification FlexLayoutTest::kCustomFlex =;
const FlexSpecification FlexLayoutTest::kCustomFlexSnapToZero =;

}  // namespace

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

TEST_F(FlexLayoutTest, GetMinimumSize_Empty) {}

TEST_F(FlexLayoutTest, GetMinimumSize_Empty_ViewInsets_Horizontal) {}

TEST_F(FlexLayoutTest, GetMinimumSize_Empty_ViewInsets_Vertical) {}

TEST_F(FlexLayoutTest, GetMinimumSize_Empty_InternalMargin_Collapsed) {}

TEST_F(FlexLayoutTest, GetMinimumSize_Empty_InternalMargin_NotCollapsed) {}

TEST_F(FlexLayoutTest,
       GetMinimumSize_Empty_InternalMargin_DefaultMarginHasNoEffect) {}

TEST_F(FlexLayoutTest, GetMinimumSize_MinimumCross_Horizontal) {}

TEST_F(FlexLayoutTest, GetMinimumSize_MinimumCross_Vertical) {}

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

TEST_F(FlexLayoutTest, Layout_VisibilitySetBeforeInstall) {}

TEST_F(FlexLayoutTest, Layout_VisibilitySetAfterInstall) {}

TEST_F(FlexLayoutTest, Layout_VisibilitySetBeforeAdd) {}

TEST_F(FlexLayoutTest, Layout_VisibilitySetAfterAdd) {}

TEST_F(FlexLayoutTest,
       Layout_ViewVisibilitySetNotContingentOnActualVisibility) {}

TEST_F(FlexLayoutTest, Layout_Exlcude) {}

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

TEST_F(FlexLayoutTest, LayoutSingleView_Horizontal) {}

TEST_F(FlexLayoutTest, LayoutSingleView_Vertical) {}

TEST_F(FlexLayoutTest, LayoutMultipleViews_Horizontal_CrossStart) {}

TEST_F(FlexLayoutTest, LayoutMultipleViews_Horizontal_CrossCenter) {}

TEST_F(FlexLayoutTest, LayoutMultipleViews_Horizontal_CrossEnd) {}

TEST_F(FlexLayoutTest, LayoutMultipleViews_Horizontal_CrossStretch) {}

TEST_F(FlexLayoutTest, LayoutMultipleViews_Vertical_CrossStart) {}

TEST_F(FlexLayoutTest, LayoutMultipleViews_Vertical_CrossCenter) {}

TEST_F(FlexLayoutTest, LayoutMultipleViews_Vertical_CrossEnd) {}

TEST_F(FlexLayoutTest, LayoutMultipleViews_Vertical_CrossStretch) {}

TEST_F(FlexLayoutTest,
       LayoutMultipleViews_MarginAndSpacing_NoCollapse_Horizontal) {}

TEST_F(FlexLayoutTest,
       LayoutMultipleViews_MarginAndSpacing_NoCollapse_Vertical) {}

TEST_F(FlexLayoutTest,
       LayoutMultipleViews_MarginAndSpacing_Collapse_Horizontal) {}

TEST_F(FlexLayoutTest, LayoutMultipleViews_MarginAndSpacing_Collapse_Vertical) {}

TEST_F(FlexLayoutTest, LayoutMultipleViews_InteriorPadding) {}

TEST_F(FlexLayoutTest, LayoutMultipleViews_InteriorPadding_Margins) {}

TEST_F(FlexLayoutTest, LayoutMultipleViews_InteriorPadding_Additive) {}

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

TEST_F(FlexLayoutTest, HeightForWidth_Vertical_CrossStart) {}

TEST_F(FlexLayoutTest,
       HeightForWidth_Vertical_CrossStretch_WidthChangesHeight) {}

TEST_F(FlexLayoutTest, HeightForWidth_Vertical_CrossStretch_FlexPreferredSize) {}

TEST_F(FlexLayoutTest, HeightForWidth_Vertical_CrossStretch_FlexLarger) {}

TEST_F(FlexLayoutTest, HeightForWidth_Vertical_CrossStretch_FlexSmaller) {}

TEST_F(FlexLayoutTest, HeightForWidth_Horizontal_PreferredSize) {}

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

TEST_F(FlexLayoutTest, Layout_HostInsets_Horizontal) {}

TEST_F(FlexLayoutTest, Layout_HostInsets_Vertical) {}

TEST_F(FlexLayoutTest, Layout_HostInsets_Horizontal_Leading) {}

TEST_F(FlexLayoutTest, Layout_HostInsets_Vertical_Leading) {}

TEST_F(FlexLayoutTest, Layout_HostInsets_Horizontal_Center) {}

TEST_F(FlexLayoutTest, Layout_HostInsets_Vertical_Center) {}

TEST_F(FlexLayoutTest, Layout_HostInsets_Horizontal_End) {}

TEST_F(FlexLayoutTest, Layout_HostInsets_Vertical_End) {}

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

TEST_F(FlexLayoutTest, SetIncludeHostInsetsInLayout_NoChange) {}

TEST_F(FlexLayoutTest, SetIncludeHostInsetsInLayout_CollapseIntoInsets) {}

TEST_F(FlexLayoutTest, SetIncludeHostInsetsInLayout_OverlapInsets) {}

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

TEST_F(FlexLayoutTest, SetIgnoreDefaultMainAxisMargins_IgnoresDefaultMargins) {}

TEST_F(FlexLayoutTest,
       SetIgnoreDefaultMainAxisMargins_IncludesExplicitMargins) {}

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

TEST_F(FlexLayoutTest, Layout_CrossStart) {}

TEST_F(FlexLayoutTest, Layout_CrossCenter) {}

TEST_F(FlexLayoutTest, Layout_CrossEnd) {}

TEST_F(FlexLayoutTest, Layout_CrossStretch) {}

TEST_F(FlexLayoutTest, Layout_AlignStart) {}

TEST_F(FlexLayoutTest, Layout_AlignCenter) {}

TEST_F(FlexLayoutTest, Layout_AlignEnd) {}

TEST_F(FlexLayoutTest, Layout_AddDroppedMargins) {}

TEST_F(FlexLayoutTest, Layout_VerticalAlign_WiderThanTall) {}

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

TEST_F(FlexLayoutTest, Layout_IgnoreMinimumSize_DropViews) {}

TEST_F(FlexLayoutTest, Layout_IgnoreMinimumSize_DropInOrder) {}

TEST_F(FlexLayoutTest, Layout_IgnoreMinimumSize_DropInOrder_DefaultFlex) {}

TEST_F(FlexLayoutTest, Layout_IgnoreMinimumSize_DropByPriority) {}

TEST_F(FlexLayoutTest, Layout_Flex_OneViewScales) {}

TEST_F(FlexLayoutTest, Layout_Flex_OneViewScales_BelowMinimum) {}

TEST_F(FlexLayoutTest,
       Layout_Flex_OneViewScales_CausesSubsequentControlToDropOut) {}

TEST_F(FlexLayoutTest,
       Layout_Flex_OneViewScales_CausesSubsequentFlexControlToDropOut) {}

TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_EqualWeight) {}

TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_DefaultFlex) {}

TEST_F(FlexLayoutTest,
       Layout_Flex_TwoChildViews_UnequalWeight_FirstHigher_FlexSmaller) {}

TEST_F(FlexLayoutTest,
       Layout_Flex_TwoChildViews_UnequalWeight_SecondHigher_FlexSmaller) {}

// 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) {}

// 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) {}

// 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) {}

TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_Priority) {}

TEST_F(FlexLayoutTest,
       Layout_Flex_TwoChildViews_Priority_LowerPriorityDropsOut) {}

TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedSnapToMinimum) {}

TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedScaleToMinimumSnapToZero) {}

TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedScaleToZero) {}

TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedSnapToMinimum1D) {}

TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedScaleToMinimumSnapToZero1D) {}

TEST_F(FlexLayoutTest, Layout_FlexRule_UnboundedScaleToZero1D) {}

// Tests that views allowed to scale up to their maximum size will do so.
TEST_F(FlexLayoutTest, Layout_FlexRule_ScaleToMaximum) {}

// Tests that views allowed to scale up to their maximum size will do so.
TEST_F(FlexLayoutTest, Layout_FlexRule_ScaleToMaximum_WithOrder) {}

// 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) {}

// 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) {}

// 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) {}

// 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) {}

// 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) {}

TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_FlexAlignment_Start) {}

TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_FlexAlignment_End) {}

TEST_F(FlexLayoutTest, Layout_Flex_TwoChildViews_FlexAlignment_Center) {}

TEST_F(FlexLayoutTest, Layout_FlexRule_CustomFlexRule) {}

TEST_F(FlexLayoutTest, Layout_FlexRule_CustomFlexRule_WithNonFlex) {}

TEST_F(FlexLayoutTest, Layout_FlexRule_CustomFlexRule_ShrinkToZero) {}

TEST_F(FlexLayoutTest, Layout_OnlyCallsSetViewVisibilityWhenNecessary) {}

TEST_F(FlexLayoutTest, Layout_Vertical_ZeroWidthNonZeroHeight) {}

TEST_F(FlexLayoutTest, Layout_Vertical_ZeroWidthZeroHeight) {}

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

TEST_F(FlexLayoutTest, GetAvailableSize_NoFlex) {}

TEST_F(FlexLayoutTest, GetAvailableSize_NoFlex_Margins) {}

TEST_F(FlexLayoutTest, GetAvailableSize_NoFlex_ExtraSize) {}

TEST_F(FlexLayoutTest, GetAvailableSize_NoFlex_Vertical) {}

TEST_F(FlexLayoutTest, GetAvailableSize_Flex_AllSameSize) {}

TEST_F(FlexLayoutTest, GetAvailableSize_Flex_VariedMinimumSizes) {}

TEST_F(FlexLayoutTest, GetAvailableSize_Flex_HiddenViews) {}

TEST_F(FlexLayoutTest, GetAvailableSize_Flex_DifferentWeights) {}

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

TEST_F(FlexLayoutTest, FlexAllocationOrderNormal) {}

TEST_F(FlexLayoutTest, FlexAllocationOrderReverse) {}

TEST_F(FlexLayoutTest, FlexAllocationOrderNormalWithExcess) {}

TEST_F(FlexLayoutTest, FlexAllocationOrderReverseWithExcess) {}

// 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) {}

// 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) {}

// 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) {}

// 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) {}

// 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) {}

// 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) {}

}  // 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) {}

// 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) {}

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

TEST_F(FlexLayoutTest, IndividualCrossAxisAlignmentInHorizontalLayoutTest) {}

TEST_F(FlexLayoutTest, IndividualCrossAxisAlignmentInVerticalLayoutTest) {}

TEST_F(FlexLayoutTest, PreferredSizeMutationTest) {}

TEST_F(FlexLayoutTest, PreferredSizeMutationTest2) {}

TEST_F(FlexLayoutTest, ZeroPreferedSizeView) {}

// 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 {};

TEST_F(FlexLayoutCrossAxisFitTest, Layout_CrossStretch) {}

TEST_F(FlexLayoutCrossAxisFitTest, Layout_CrossStart) {}

TEST_F(FlexLayoutCrossAxisFitTest, Layout_CrossCenter) {}

TEST_F(FlexLayoutCrossAxisFitTest, Layout_CrossEnd) {}

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

class NestedFlexLayoutTest : public FlexLayoutTest {};

TEST_F(NestedFlexLayoutTest, SetVisible_UpdatesLayout) {}

TEST_F(NestedFlexLayoutTest, AddChild_UpdatesLayout) {}

TEST_F(NestedFlexLayoutTest, RemoveChild_UpdatesLayout) {}

TEST_F(NestedFlexLayoutTest, Layout_OppositeOrientation) {}

TEST_F(NestedFlexLayoutTest, Layout_SameOrientation) {}

TEST_F(NestedFlexLayoutTest, Layout_Flex) {}

TEST_F(NestedFlexLayoutTest, UsingDefaultFlexRule) {}

TEST_F(NestedFlexLayoutTest, UnboundedZeroSize) {}

namespace {

struct DirectionalFlexRuleTestParamRules {};

struct DirectionalFlexRuleTestParam {};

// No flex in cross-axis direction.
static const DirectionalFlexRuleTestParamRules kNoCrossFlex =;

// Drop out on main axis, flex on cross axis.
static const DirectionalFlexRuleTestParamRules kMainDropOutCrossFlex =;

// Preferred height-for-width on main axis, scale to minimum snap to zero on
// cross axis. (Note: Vertical only!)
static const DirectionalFlexRuleTestParamRules kFlexUseHeightForWidth =;

const DirectionalFlexRuleTestParam DirectionalFlexRuleTestParamList[] =;

}  // anonymous namespace

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

TEST_P(FlexLayoutDirectionalRuleTest, TestRules) {}

INSTANTIATE_TEST_SUITE_P();

}  // namespace views