/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

#import <ComponentSnapshotTestCase/CKComponentSnapshotTestCase.h>

#import <ComponentKit/CKComponentSubclass.h>
#import <ComponentKit/CKCompositeComponent.h>
#import <ComponentKit/CKFlexboxComponent.h>
#import <ComponentKit/CKInsetComponent.h>
#import <ComponentKit/CKRatioLayoutComponent.h>
#import <ComponentKit/CKComponentLayout.h>
#import <ComponentKit/CKComponentLayoutBaseline.h>
#import <ComponentKit/CKCompositeComponent.h>

/* Sets baseline to be 10 points higher than normal. */
@interface CKCustomBaselineComponent : CKComponent

+ (instancetype)newWithView:(const CKComponentViewConfiguration &)view
                       size:(const RCComponentSize &)size
                   baseline:(CGFloat)baseline;
@end

@implementation CKCustomBaselineComponent
{
  CGFloat _baseline;
}

+ (instancetype)newWithView:(const CKComponentViewConfiguration &)view
                       size:(const RCComponentSize &)size
                   baseline:(CGFloat)baseline
{
  CKCustomBaselineComponent *c = [super newWithView:view size:size];
  if (c) {
    c->_baseline = baseline;
  }

  return c;
}

- (RCLayout)computeLayoutThatFits:(CKSizeRange)constrainedSize
{
  RCLayout parentLayout = [super computeLayoutThatFits:constrainedSize];
  NSMutableDictionary *extra = parentLayout.extra ? [parentLayout.extra mutableCopy]: [NSMutableDictionary dictionary];
  extra[kCKComponentLayoutExtraBaselineKey] = @(_baseline);
  parentLayout.extra = extra;
  return parentLayout;
}

- (BOOL)usesCustomBaseline
{
  return YES;
}

@end

static CKComponentViewConfiguration kWhiteBackgroundView = {
  [UIView class], {{@selector(setBackgroundColor:), [UIColor whiteColor]}}
};

static CKComponentViewConfiguration kLightGrayBackgroundView = {
  [UIView class], {{@selector(setBackgroundColor:), [UIColor lightGrayColor]}}
};

@interface CKFlexboxComponentSnapshotTests : CKComponentSnapshotTestCase

@property (nonatomic, assign) BOOL useDeepYogaTrees;

@end

@implementation CKFlexboxComponentSnapshotTests

- (void)setUp
{
  [super setUp];
  self.recordMode = NO;
  _useDeepYogaTrees = NO;
}

- (void)testUnderflowBehaviors
{
  // width 300px; height 0-300px
  static CKSizeRange kSize = {{300, 0}, {300, 300}};
  CKSnapshotVerifyComponent([self _layoutWithJustify:CKFlexboxJustifyContentStart flexFactor:0], kSize, @"justifyStart");
  CKSnapshotVerifyComponent([self _layoutWithJustify:CKFlexboxJustifyContentCenter flexFactor:0], kSize, @"justifyCenter");
  CKSnapshotVerifyComponent([self _layoutWithJustify:CKFlexboxJustifyContentEnd flexFactor:0], kSize, @"justifyEnd");
  CKSnapshotVerifyComponent([self _layoutWithJustify:CKFlexboxJustifyContentStart flexFactor:1], kSize, @"flex");
}

- (void)testOverflowBehaviors
{
  // width 110px; height 0-300px
  static CKSizeRange kSize = {{110, 0}, {110, 300}};
  CKSnapshotVerifyComponent([self _layoutWithJustify:CKFlexboxJustifyContentStart flexFactor:0], kSize, @"justifyStart");
  CKSnapshotVerifyComponent([self _layoutWithJustify:CKFlexboxJustifyContentCenter flexFactor:0], kSize, @"justifyCenter");
  CKSnapshotVerifyComponent([self _layoutWithJustify:CKFlexboxJustifyContentEnd flexFactor:0], kSize, @"justifyEnd");
  CKSnapshotVerifyComponent([self _layoutWithJustify:CKFlexboxJustifyContentStart flexFactor:1], kSize, @"flex");
}

- (void)testShrinkingBehaviourWithFullFlexGrow
{
  auto const c = CK::FlexboxComponentBuilder()
  .width(500)
  .height(500)
  .direction(CKFlexboxDirectionRow)
  .alignItems(CKFlexboxAlignItemsStart)
  .useDeepYogaTrees(_useDeepYogaTrees)
  .child(CK::ComponentBuilder()
          .viewClass([UIView class])
          .backgroundColor([UIColor greenColor])
          .width(500)
          .height(500)
          .build())
          .flexGrow(0)
          .flexShrink(1)
  .child(CK::ComponentBuilder()
          .viewClass([UIView class])
          .backgroundColor([UIColor redColor])
          .width(500)
          .height(500)
          .build())
          .flexGrow(1)
          .flexShrink(1)
  .build();

  static CKSizeRange kSize = {{500, 500}, {500, 500}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testCorrectnessOfDeeplyNestedFlexboxHierarchies
{
  CKComponent *(^component)(UIColor *, const RCComponentSize &) = ^CKComponent *(UIColor *color, const RCComponentSize &size) {
    return CK::ComponentBuilder()
            .viewClass([UIView class])
            .backgroundColor(color)
            .size(size)
            .build();
  };
  CKFlexboxComponentChild(^leaf)(UIColor *, const RCComponentSize &) = ^CKFlexboxComponentChild (UIColor *color, const RCComponentSize &size) {
    return {
      .component = component(color, size),
      .position = {
        .type = CKFlexboxPositionTypeRelative,
      },
    };
  };
  BOOL useDeepYogaTrees = _useDeepYogaTrees;

  CKComponent *c =
  [CKFlexboxComponent
   newWithView:{}
   size:{500,500}
   style:{
     .direction = CKFlexboxDirectionRow,
     .alignItems = CKFlexboxAlignItemsStart,
     .padding = {
       .top = 10,
       .start = 10,
       .end = 10,
       .bottom = 10,
     },
     .useDeepYogaTrees = useDeepYogaTrees,
   }
   children:{
     {
       .component =
       [CKCompositeComponent
        newWithComponent:
        [CKFlexboxComponent
         newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor lightGrayColor]}}}
         size:{NAN,NAN}
         style:{
           .direction = CKFlexboxDirectionRow,
           .alignItems = CKFlexboxAlignItemsStretch,
           .useDeepYogaTrees = useDeepYogaTrees,
         }
         children:{
           {
             .component =
             [CKFlexboxComponent
              newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor brownColor]}}}
              size:{100,NAN}
              style:{
                .border = {
                  .top = 5,
                  .start = 5,
                  .end = 5,
                  .bottom = 5,
                },
                .useDeepYogaTrees = useDeepYogaTrees,
              }
              children:{
                leaf([UIColor redColor], {100,100}),
                leaf([UIColor greenColor], {100,100}),
                leaf([UIColor blueColor], {100,100}),
                leaf([UIColor redColor], {100,100}),
              }]
           },
           leaf([UIColor grayColor], {100,NAN}),
         }]],
       .alignSelf = CKFlexboxAlignSelfStretch,
     },
     {
       .component =
       [CKFlexboxComponent
        newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor lightGrayColor]}}}
        size:{100,NAN}
        style:{
          .alignItems = CKFlexboxAlignItemsStretch,
          .padding = {
            .top = 10,
            .start = 10,
            .end = 10,
            .bottom = 10,
          },
          .useDeepYogaTrees = useDeepYogaTrees,
        }
        children:{
          {
            .component =
            [CKFlexboxComponent
             newWithView:{}
             size:{NAN,100}
             style:{
               .direction = CKFlexboxDirectionRow,
               .alignItems = CKFlexboxAlignItemsStretch,
               .useDeepYogaTrees = useDeepYogaTrees,
             }
             children:{
               {
                 .component = component([UIColor redColor], {NAN,NAN}),
                 .flexGrow = 0.2f,
                 .flexShrink = 1.0f,
                 .position = {
                   .type = CKFlexboxPositionTypeRelative,
                 },
               },
               {
                 .component = [CKCompositeComponent newWithComponent:component([UIColor blueColor], {NAN,NAN})],
                 .flexGrow = 0.2f,
                 .flexShrink = 1.0f,
                 .position = {
                   .type = CKFlexboxPositionTypeRelative,
                 },
               },
               {
                 .component = component([UIColor greenColor], {NAN,NAN}),
                 .flexGrow = 1.0f,
                 .flexShrink = 1.0f,
                 .position = {
                   .type = CKFlexboxPositionTypeRelative,
                 },
               },
             }]
          },
          leaf([UIColor yellowColor], {NAN,50}),
          leaf([UIColor magentaColor], {NAN,50}),
          leaf([UIColor redColor], {NAN,50}),
          leaf([UIColor blueColor], {NAN,50}),
          leaf([UIColor greenColor], {NAN,50}),
        }],
       .flexGrow = 1.0f,
       .flexShrink = 1.0f,
       .alignSelf = CKFlexboxAlignSelfStretch,
     },
     leaf([UIColor grayColor], {100,100}),
   }];

  static CKSizeRange kSize = {{500, 500}, {500, 500}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testOverflowBehaviorsWhenAllFlexShrinkComponentsHaveBeenClampedToZeroButViolationStillExists
{
  auto const c = CK::FlexboxComponentBuilder()
  .viewClass([UIView class])
  .backgroundColor([UIColor whiteColor])
  .alignItems(CKFlexboxAlignItemsStart)
  .direction(CKFlexboxDirectionRow)
  .useDeepYogaTrees(_useDeepYogaTrees)
  .child(CK::ComponentBuilder()
          .viewClass([UIView class])
          .backgroundColor([UIColor redColor])
          .width(50)
          .height(50)
          .build())
          .flexShrink(0)
  .child(CK::ComponentBuilder()
          .viewClass([UIView class])
          .backgroundColor([UIColor blueColor])
          .width(50)
          .height(50)
          .build())
          .flexShrink(1)
  .child(CK::ComponentBuilder()
          .viewClass([UIView class])
          .backgroundColor([UIColor greenColor])
          .width(50)
          .height(50)
          .build())
          .flexShrink(0)
  .build();

  // Width is 75px--that's less than the sum of the widths of the child components, which is 100px.
  static CKSizeRange kSize = {{75, 0}, {75, 150}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testFlexWithUnequalIntrinsicSizes
{
  auto const c = CK::FlexboxComponentBuilder()
  .viewClass([UIView class])
  .backgroundColor([UIColor whiteColor])
  .alignItems(CKFlexboxAlignItemsStart)
  .direction(CKFlexboxDirectionRow)
  .useDeepYogaTrees(_useDeepYogaTrees)
  .child(CK::ComponentBuilder()
          .viewClass([UIView class])
          .backgroundColor([UIColor redColor])
          .width(50)
          .height(50)
          .build())
          .flexShrink(1)
          .flexGrow(1)
  .child(CK::ComponentBuilder()
          .viewClass([UIView class])
          .backgroundColor([UIColor blueColor])
          .width(150)
          .height(150)
          .build())
          .flexShrink(1)
          .flexGrow(1)
  .child(CK::ComponentBuilder()
          .viewClass([UIView class])
          .backgroundColor([UIColor greenColor])
          .width(50)
          .height(50)
          .build())
          .flexShrink(1)
          .flexGrow(1)
  .build();

  // width 300px; height 0-150px.
  static CKSizeRange kUnderflowSize = {{300, 0}, {300, 150}};
  CKSnapshotVerifyComponent(c, kUnderflowSize, @"underflow");

  // width 200px; height 0-150px.
  static CKSizeRange kOverflowSize = {{200, 0}, {200, 150}};
  CKSnapshotVerifyComponent(c, kOverflowSize, @"overflow");
}

- (void)testCrossAxisSizeBehaviors
{
  auto const c = CK::FlexboxComponentBuilder()
  .viewClass([UIView class])
  .backgroundColor([UIColor whiteColor])
  .alignItems(CKFlexboxAlignItemsStart)
  .direction(CKFlexboxDirectionColumn)
  .useDeepYogaTrees(_useDeepYogaTrees)
  .child(CK::ComponentBuilder()
          .viewClass([UIView class])
          .backgroundColor([UIColor redColor])
          .width(50)
          .height(50)
          .build())
  .child(CK::ComponentBuilder()
          .viewClass([UIView class])
          .backgroundColor([UIColor blueColor])
          .width(100)
          .height(50)
          .build())
  .child(CK::ComponentBuilder()
          .viewClass([UIView class])
          .backgroundColor([UIColor greenColor])
          .width(150)
          .height(50)
          .build())
  .build();

  // width 0-300px; height 300px
  static CKSizeRange kVariableHeight = {{0, 300}, {300, 300}};
  CKSnapshotVerifyComponent(c, kVariableHeight, @"variableHeight");

  // width 300px; height 300px
  static CKSizeRange kFixedHeight = {{300, 300}, {300, 300}};
  CKSnapshotVerifyComponent(c, kFixedHeight, @"fixedHeight");
}

- (void)testSpacing
{
  auto const c = CK::FlexboxComponentBuilder()
  .viewClass([UIView class])
  .backgroundColor([UIColor whiteColor])
  .alignItems(CKFlexboxAlignItemsStart)
  .direction(CKFlexboxDirectionColumn)
  .spacing(10)
  .useDeepYogaTrees(_useDeepYogaTrees)
  .child(CK::ComponentBuilder()
          .viewClass([UIView class])
          .backgroundColor([UIColor redColor])
          .width(50)
          .height(50)
          .build())
  .child(CK::ComponentBuilder()
          .viewClass([UIView class])
          .backgroundColor([UIColor blueColor])
          .width(100)
          .height(50)
          .build())
  .child(CK::ComponentBuilder()
          .viewClass([UIView class])
          .backgroundColor([UIColor greenColor])
          .width(150)
          .height(50)
          .build())
  .build();

  // width 0-300px; height 300px
  static CKSizeRange kVariableHeight = {{0, 300}, {300, 300}};
  CKSnapshotVerifyComponent(c, kVariableHeight, @"variableHeight");
}

- (void)testNegativeSpacing
{
  auto const c = CK::FlexboxComponentBuilder()
  .viewClass([UIView class])
  .backgroundColor([UIColor whiteColor])
  .alignItems(CKFlexboxAlignItemsStart)
  .direction(CKFlexboxDirectionColumn)
  .spacing(-10)
  .useDeepYogaTrees(_useDeepYogaTrees)
  .child(CK::ComponentBuilder()
          .viewClass([UIView class])
          .backgroundColor([UIColor redColor])
          .width(50)
          .height(50)
          .build())
  .child(CK::ComponentBuilder()
          .viewClass([UIView class])
          .backgroundColor([UIColor blueColor])
          .width(100)
          .height(50)
          .build())
  .child(CK::ComponentBuilder()
          .viewClass([UIView class])
          .backgroundColor([UIColor greenColor])
          .width(150)
          .height(50)
          .build())
  .build();

  // width 0-300px; height 300px
  static CKSizeRange kVariableHeight = {{0, 300}, {300, 300}};
  CKSnapshotVerifyComponent(c, kVariableHeight, @"variableHeight");
}

- (void)testSpacingWithChildrenHavingNilComponents
{
  // This should take a zero height since all children have a nil component. If it takes a height > 0, a blue border
  // will show up, hence failing the test.

  static const CKComponentViewAttribute borderAttribute = {"CKFlexboxComponentTest.border", ^(UIView *view, id value) {
    view.layer.borderColor = [UIColor blueColor].CGColor;
    view.layer.borderWidth = 3.0f;
  }};

  auto const c = CK::FlexboxComponentBuilder()
  .viewClass([UIView class])
  .attributes({{borderAttribute, nil}})
  .direction(CKFlexboxDirectionColumn)
  .spacing(10)
  .alignItems(CKFlexboxAlignItemsStretch)
  .useDeepYogaTrees(_useDeepYogaTrees)
  .child(nil)
  .child(nil)
  .build();

  // width 300px; height 0-300px
  static CKSizeRange kVariableHeight = {{300, 0}, {300, 300}};
  CKSnapshotVerifyComponentWithInsets(c, kVariableHeight, UIEdgeInsetsMake(10, 10, 10, 10), @"variableHeight");
}

- (void)testComponentSpacing
{
  // width 0-INF; height 0-INF
  static CKSizeRange kAnySize = {{0, 0}, {INFINITY, INFINITY}};

  CKComponent *spacingBefore =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}]
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,70}],
       .spacingBefore = 10
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,90}],
       .spacingBefore = 20
     },
   }];
  CKSnapshotVerifyComponent(spacingBefore, kAnySize, @"spacingBefore");

  CKComponent *spacingAfter =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}]
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,70}],
       .spacingAfter = 10
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,90}],
       .spacingAfter = 20
     },
   }];
  CKSnapshotVerifyComponent(spacingAfter, kAnySize, @"spacingAfter");

  CKComponent *spacingBalancedOut =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .spacing = 10,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}]
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,70}],
       .spacingBefore = -10,
       .spacingAfter = -10
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,90}],
     },
   }];
  CKSnapshotVerifyComponent(spacingBalancedOut, kAnySize, @"spacingBalancedOut");
}

- (void)testHorizontalReverseSpacing
{
  // width 0-INF; height 0-INF
  static CKSizeRange kAnySize = {{0, 0}, {INFINITY, INFINITY}};

  CKComponent *spacingBefore =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRowReverse,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}]
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,70}],
       .spacingBefore = 10
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,90}],
       .spacingBefore = 20
     },
   }];
  CKSnapshotVerifyComponent(spacingBefore, kAnySize, @"spacingBefore");

  CKComponent *spacingAfter =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRowReverse,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}]
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,70}],
       .spacingAfter = 10
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,90}],
       .spacingAfter = 20
     },
   }];
  CKSnapshotVerifyComponent(spacingAfter, kAnySize, @"spacingAfter");

  CKComponent *spacingBalancedOut =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRowReverse,
     .spacing = 10,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}]
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,70}],
       .spacingBefore = -10,
       .spacingAfter = -10
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,90}],
     },
   }];
  CKSnapshotVerifyComponent(spacingBalancedOut, kAnySize, @"spacingBalancedOut");
}

- (void)testVerticalReverseSpacing
{
  // width 0-INF; height 0-INF
  static CKSizeRange kAnySize = {{0, 0}, {INFINITY, INFINITY}};

  CKComponent *spacingBefore =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumnReverse,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}]
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,70}],
       .spacingBefore = 10
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,90}],
       .spacingBefore = 20
     },
   }];
  CKSnapshotVerifyComponent(spacingBefore, kAnySize, @"spacingBefore");

  CKComponent *spacingAfter =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumnReverse,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}]
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,70}],
       .spacingAfter = 10
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,90}],
       .spacingAfter = 20
     },
   }];
  CKSnapshotVerifyComponent(spacingAfter, kAnySize, @"spacingAfter");

  CKComponent *spacingBalancedOut =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumnReverse,
     .spacing = 10,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}]
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,70}],
       .spacingBefore = -10,
       .spacingAfter = -10
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,90}],
     },
   }];
  CKSnapshotVerifyComponent(spacingBalancedOut, kAnySize, @"spacingBalancedOut");
}

- (void)testJustifiedCenterWithComponentSpacing
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .justifyContent = CKFlexboxJustifyContentCenter,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}], 0},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,70}], 20},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,90}], 30},
   }];

  // width 0-300px; height 300px
  static CKSizeRange kVariableHeight = {{0, 300}, {300, 300}};
  CKSnapshotVerifyComponent(c, kVariableHeight, @"variableHeight");
}

- (void)testZOrderNoChangeLayoutOrder
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .spacing = -10,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}],
       .zIndex = 0},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,50}],
       .zIndex = 1},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,50}],
       .zIndex = 2},
   }];

  // width 0-300px; height 300px
  static CKSizeRange kVariableHeight = {{0, 300}, {300, 300}};
  CKSnapshotVerifyComponent(c, kVariableHeight, @"variableHeight");
}

- (void)testZOrderPartialChangeLayoutOrder
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .spacing = -10,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}],
       .zIndex = 0},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,50}],
       .zIndex = 1},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,50}],
       .zIndex = 0},
   }];

  // width 0-300px; height 300px
  static CKSizeRange kVariableHeight = {{0, 300}, {300, 300}};
  CKSnapshotVerifyComponent(c, kVariableHeight, @"variableHeight");
}

- (void)testZOrderChangeLayoutOrder
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .spacing = -10,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}],
       .zIndex = 2},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,50}],
       .zIndex = 1},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,50}],
       .zIndex = 0},
   }];

  // width 0-300px; height 300px
  static CKSizeRange kVariableHeight = {{0, 300}, {300, 300}};
  CKSnapshotVerifyComponent(c, kVariableHeight, @"variableHeight");
}

/**
 The expected layout is [-------red------|---blue--], with no gray visible.
 The blue doesn't extend all the way.
 Fiddle: https://jsfiddle.net/g0kaahb7/1/
 */
- (void)testMinWidthIsRespected
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kLightGrayBackgroundView
   size:{.width=120}
   style:{
     .direction = CKFlexboxDirectionRow,
     .alignItems = CKFlexboxAlignItemsStretch,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {[CKComponent
       newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}}
       size:{
         .minWidth = 90,
         .height = 50
       }],
       .flexGrow = 1,
       .flexShrink = 1,
       .flexBasis = 80
     },
     {[CKComponent
       newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}}
       size:{
         .height = 50
       }],
       .flexGrow = 1,
       .flexShrink = 1,
       .flexBasis = 80
     },
   }];

  static CKSizeRange kSize = {{0, 50}, {120, 50}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

/**
 This just crashes. It's supposed to look like the fiddle below, where the blue side is longer
 Fiddle: https://jsfiddle.net/62h401ce/1/
 */
- (void)testMinAndMaxWidthTakePriorityOverPreferredSize
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kLightGrayBackgroundView
   size:{.width=120}
   style:{
     .direction = CKFlexboxDirectionRow,
     .alignItems = CKFlexboxAlignItemsStretch,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {[CKComponent
       newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}}
       size:{
         .minWidth = 60,
         .height = 50
       }],
       .flexGrow = 1,
       .flexShrink = 0,
       .flexBasis = 0
     },
     {[CKComponent
       newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}}
       size:{
         .height = 50,
         .maxWidth = 20,
       }],
       .flexGrow = 1,
       .flexShrink = 0,
       .flexBasis = RCRelativeDimension::Percent(0.5)
     },
   }];

  static CKSizeRange kSize = {{0, 50}, {120, 50}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testComponentThatChangesCrossSizeWhenMainSizeIsFlexed
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       CK::RatioLayoutComponentBuilder()
       .ratio(1.5)
       .component(
         CK::ComponentBuilder()
         .viewClass([UIView class])
         .backgroundColor([UIColor blueColor])
         .height(150)
         .build()
       )
       .build(),
       .flexBasis = RCRelativeDimension::Percent(1),
       .flexGrow = 1,
       .flexShrink = 1
     },
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}]},
   }];

  static CKSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}};
  CKSnapshotVerifyComponent(c, kFixedWidth, nil);
}

- (void)testAlignContentStart
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kLightGrayBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .alignContent = CKFlexboxAlignContentStart,
     .wrap = CKFlexboxWrapWrap,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{100,20}],
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,20}],
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{100,20}],
     },
   }];

  static CKSizeRange kFixedWidthAndHeight = {{100, 100}, {100, 100}};
  CKSnapshotVerifyComponent(c, kFixedWidthAndHeight, nil);
}

- (void)testAlignContentEnd
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kLightGrayBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .alignContent = CKFlexboxAlignContentEnd,
     .wrap = CKFlexboxWrapWrap,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{100,20}],
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,20}],
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{100,20}],
     },
   }];

  static CKSizeRange kFixedWidthAndHeight = {{100, 100}, {100, 100}};
  CKSnapshotVerifyComponent(c, kFixedWidthAndHeight, nil);
}

- (void)testAlignContentCenter
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kLightGrayBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .alignContent = CKFlexboxAlignContentCenter,
     .wrap = CKFlexboxWrapWrap,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{100,20}],
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,20}],
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{100,20}],
     },
   }];

  static CKSizeRange kFixedWidthAndHeight = {{100, 100}, {100, 100}};
  CKSnapshotVerifyComponent(c, kFixedWidthAndHeight, nil);
}

- (void)testAlignContentSpaceBetween
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kLightGrayBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .alignContent = CKFlexboxAlignContentSpaceBetween,
     .wrap = CKFlexboxWrapWrap,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{100,20}],
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,20}],
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{100,20}],
     },
   }];

  static CKSizeRange kFixedWidthAndHeight = {{100, 100}, {100, 100}};
  CKSnapshotVerifyComponent(c, kFixedWidthAndHeight, nil);
}

- (void)testAlignContentSpaceAround
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kLightGrayBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .alignContent = CKFlexboxAlignContentSpaceAround,
     .wrap = CKFlexboxWrapWrap,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{100,20}],
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,20}],
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{100,20}],
     },
   }];

  static CKSizeRange kFixedWidthAndHeight = {{100, 100}, {100, 100}};
  CKSnapshotVerifyComponent(c, kFixedWidthAndHeight, nil);
}

- (void)testAlignContentStretch
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kLightGrayBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .alignContent = CKFlexboxAlignContentStretch,
     .wrap = CKFlexboxWrapWrap,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{100,20}],
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,20}],
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{100,20}],
     },
   }];

  static CKSizeRange kFixedWidthAndHeight = {{100, 100}, {100, 100}};
  CKSnapshotVerifyComponent(c, kFixedWidthAndHeight, nil);
}

- (void)testAlignBaseline
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:{
     [UIView class],
     {{@selector(setBackgroundColor:), [UIColor lightGrayColor]}},
   }
   size:{}
   style:{
     .direction = CKFlexboxDirectionRow,
     .alignItems = CKFlexboxAlignItemsBaseline,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,100}],
       .flexShrink = 1,
       .useHeightAsBaseline = YES,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,50}],
       .flexShrink = 1,
       .useHeightAsBaseline = YES,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,75}],
       .flexShrink = 1,
       .useHeightAsBaseline = YES,
     },
   }];

  static CKSizeRange kFixedWidthAndHeight = {{200, 200}, {200, 200}};
  CKSnapshotVerifyComponent(c, kFixedWidthAndHeight, nil);
}

- (void)testCustomBaselineComponent
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:{
     [UIView class],
     {{@selector(setBackgroundColor:), [UIColor lightGrayColor]}},
   }
   size:{}
   style:{
     .direction = CKFlexboxDirectionRow,
     .alignItems = CKFlexboxAlignItemsBaseline,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,100}],
       .flexShrink = 1,
       .useHeightAsBaseline = YES,
     },
     {
       [CKCustomBaselineComponent
        newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}}
        size:{50,50}
        baseline:40],
       .flexShrink = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,75}],
       .flexShrink = 1,
       .useHeightAsBaseline = YES,
     },
   }];

  static CKSizeRange kFixedWidthAndHeight = {{200, 200}, {200, 200}};
  CKSnapshotVerifyComponent(c, kFixedWidthAndHeight, nil);
}

- (void)testVariableBaselines
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:{
     [UIView class],
     {{@selector(setBackgroundColor:), [UIColor lightGrayColor]}},
   }
   size:{}
   style:{
     .direction = CKFlexboxDirectionRow,
     .alignItems = CKFlexboxAlignItemsBaseline,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKCustomBaselineComponent
        newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}}
        size:{50,50}
        baseline:30],
       .flexShrink = 1,
     },
     {
       [CKCustomBaselineComponent
        newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}}
        size:{50,50}
        baseline:40],
       .flexShrink = 1,
     },
     {
       [CKCustomBaselineComponent
        newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}}
        size:{50,50}
        baseline:10],
       .flexShrink = 1,
     },
   }];

  static CKSizeRange kFixedWidthAndHeight = {{200, 200}, {200, 200}};
  CKSnapshotVerifyComponent(c, kFixedWidthAndHeight, nil);
}

- (void)testContainerPadding
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kLightGrayBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .padding = {
       .top = 20,
       .start = 30,
       .end = 10,
       .bottom = 5
     },
    .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{75,75}],
       .flexShrink = 1,
     },
   }];

  static CKSizeRange kSize = {{0, 0}, {300, INFINITY}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testChildPadding
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kLightGrayBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{.minWidth = 20, .minHeight = 20}],
       .padding = {
         .end = 10,
         .start = 20,
         .top = 15,
         .bottom = 20,
       },
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{.minWidth = 20, .minHeight = 20}],
     },
   }];

  static CKSizeRange kSize = {{200, 200}, {200, 200}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testPercentagePadding
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kLightGrayBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{.minWidth = 20, .minHeight = 20}],
       .padding = {
         .start = RCRelativeDimension::Percent(0.4),
         .end = RCRelativeDimension::Percent(0.2),
         .top = RCRelativeDimension::Percent(0.35),
         .bottom = RCRelativeDimension::Percent(0.4),
       },
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{.minWidth = 20, .minHeight = 20}],
     },
   }];

  static CKSizeRange kSize = {{200, 200}, {200, 200}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testUnspecifiedPaddingIsSameAsZero
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kLightGrayBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{.minWidth = 50, .minHeight = 50}],
       .padding = {},
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{.minWidth = 50, .minHeight = 50}],
       .padding = {
         .top = 10,
       },
     },
   }];

  static CKSizeRange kSize = {{200, 200}, {200, 200}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testMarginOnFlexbox
{
  auto const c =
  CK::FlexboxComponentBuilder()
    .viewClass([UIView class])
    .backgroundColor([UIColor lightGrayColor])
    .direction(CKFlexboxDirectionColumn)
    .useDeepYogaTrees(_useDeepYogaTrees)
    .child(
      CK::FlexboxComponentBuilder()
        .viewClass([UIView class])
        .backgroundColor([UIColor blueColor])
        .direction(CKFlexboxDirectionRow)
        .useDeepYogaTrees(_useDeepYogaTrees)
        .child(
          CK::ComponentBuilder()
            .viewClass([UIView class])
            .backgroundColor([UIColor redColor])
            .width(50)
            .height(50)
            .build())
        .child(
          CK::ComponentBuilder()
            .viewClass([UIView class])
            .backgroundColor([UIColor greenColor])
            .width(50)
            .height(50)
            .build())
        .build())
  .build();

  static CKSizeRange kSize = {{0,0}, {INFINITY, INFINITY}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testStandardMargins
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kLightGrayBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{.minWidth = 10, .minHeight = 10}],
       .margin = {
         .top = 10,
         .start = 30,
         .end = 10,
         .bottom = 20,
       },
       .flexGrow = 1,
     },
   }];

  static CKSizeRange kSize = {{300, 300}, {300, 300}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testMultipleMargins
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kLightGrayBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{.minWidth = 10, .minHeight = 10}],
       .margin = {
         .top = 20,
         .start = 20,
         .end = 20,
         .bottom = 20,
       },
       .flexGrow = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{.minWidth = 10, .minHeight = 10}],
       .margin = {
         .top = 30,
         .start = 30,
         .end = 30,
         .bottom = 30,
       },
       .flexGrow = 1,
     },
   }];

  static CKSizeRange kSize = {{300, 300}, {300, 300}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testMarginOverridesSpacing
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kLightGrayBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .spacing = 20,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{.minWidth = 10, .minHeight = 10}],
       .margin = {
         .top = 20,
         .start = 20,
         .end = 0,
         .bottom = 20,
       },
       .flexGrow = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{.minWidth = 10, .minHeight = 10}],
       .margin = {
         .top = 20,
         .start = 0,
         .end = 20,
         .bottom = 20,
       },
       .flexGrow = 1,
     },
   }];

  static CKSizeRange kSize = {{300, 300}, {300, 300}};
  @try {
    CKSnapshotVerifyComponent(c, kSize, nil);
  } @catch (NSException *exception) {
  }
}

- (void)testAlignSelfBaseline
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:{
     [UIView class],
     {{@selector(setBackgroundColor:), [UIColor lightGrayColor]}},
   }
   size:{}
   style:{
     .direction = CKFlexboxDirectionRow,
     .alignItems = CKFlexboxAlignItemsCenter,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,100}],
       .flexShrink = 1,
     },
     {
       [CKCustomBaselineComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,50}],
       .flexShrink = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,75}],
       .flexShrink = 1,
     },
   }];

  static CKSizeRange kFixedWidthAndHeight = {{200, 200}, {200, 200}};
  CKSnapshotVerifyComponent(c, kFixedWidthAndHeight, nil);
}

- (void)testAlignCenterWithFlexedMainDimension
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .direction = CKFlexboxDirectionColumn,
     .alignItems = CKFlexboxAlignItemsCenter,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{100,100}],
       .flexShrink = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,50}],
       .flexShrink = 1,
     },
   }];

  static CKSizeRange kFixedWidth = {{150, 0}, {150, 100}};
  CKSnapshotVerifyComponent(c, kFixedWidth, nil);
}

- (void)testAlignCenterWithIndefiniteCrossDimension
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{100,100}]
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,50}],
       .alignSelf = CKFlexboxAlignSelfCenter,
     },
   }];

  static CKSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}};
  CKSnapshotVerifyComponent(c, kFixedWidth, nil);
}

- (void)testBasicAbsolutePositioning
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kLightGrayBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKFlexboxComponent
        newWithView:kWhiteBackgroundView
        size:{}
        style:{
          .alignItems = CKFlexboxAlignItemsStart,
          .direction = CKFlexboxDirectionColumn,
          .useDeepYogaTrees = _useDeepYogaTrees,
        }
        children:{
          {
            [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,50}],
          },
          {
            .position = {
              .type = CKFlexboxPositionTypeAbsolute,
              .top = 20,
              .start = 30,
            },
            .component =
            [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,50}]
          }
        }]
     },
   }];

  static CKSizeRange kFixedWidthHeight = {{150, 100}, {150, 100}};
  CKSnapshotVerifyComponent(c, kFixedWidthHeight, nil);
}

- (void)testPercentAbsolutePositioning
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kLightGrayBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKFlexboxComponent
        newWithView:{}
        size:{150,100}
        style:{
          .alignItems = CKFlexboxAlignItemsStart,
          .direction = CKFlexboxDirectionColumn,
          .useDeepYogaTrees = _useDeepYogaTrees,
        }
        children:{
          {
            [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,50}],
          },
          {
            .position = {
              .type = CKFlexboxPositionTypeAbsolute,
              .top = RCRelativeDimension::Percent(0.2),
              .start = RCRelativeDimension::Percent(0.05),
            },
            .component =
            [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,50}]
          }
        }]
     },
   }];

  static CKSizeRange kFixedWidthHeight = {{150, 100}, {150, 100}};
  CKSnapshotVerifyComponent(c, kFixedWidthHeight, nil);
}

- (void)testRightBottomAbsolutePositioning
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kLightGrayBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKFlexboxComponent
        newWithView:{}
        size:{150,100}
        style:{
          .alignItems = CKFlexboxAlignItemsStart,
          .direction = CKFlexboxDirectionColumn,
          .useDeepYogaTrees = _useDeepYogaTrees,
        }
        children:{
          {
            [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,50}],
          },
          {
            .position = {
              .type = CKFlexboxPositionTypeAbsolute,
              .right = RCRelativeDimension::Percent(0.2),
              .bottom = RCRelativeDimension::Percent(0.2),
            },
            .component =
            [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,50}]
          }
        }]
     },
   }];

  static CKSizeRange kFixedWidthHeight = {{150, 100}, {150, 100}};
  CKSnapshotVerifyComponent(c, kFixedWidthHeight, nil);
}

- (void)testAbsolutePositionsDontOverrideFixedDimensions
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kLightGrayBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKFlexboxComponent
        newWithView:{}
        size:{150,100}
        style:{
          .alignItems = CKFlexboxAlignItemsStart,
          .direction = CKFlexboxDirectionColumn,
          .useDeepYogaTrees = _useDeepYogaTrees,
        }
        children:{
          {
            [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,50}],
          },
          {
            .position = {
              .type = CKFlexboxPositionTypeAbsolute,
              .left = 10,
              .right = 10,
            },
            .component =
            [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,50}]
          }
        }]
     },
   }];

  static CKSizeRange kFixedWidthHeight = {{150, 100}, {150, 100}};
  CKSnapshotVerifyComponent(c, kFixedWidthHeight, nil);
}

- (void)testAlignedStart
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .direction = CKFlexboxDirectionColumn,
     .justifyContent = CKFlexboxJustifyContentCenter,
     .alignItems = CKFlexboxAlignItemsStart,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}], 0},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,70}], 20},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,90}], 30},
   }];
  static CKSizeRange kExactSize = {{300, 300}, {300, 300}};
  CKSnapshotVerifyComponent(c, kExactSize, nil);
}

- (void)testAlignedEnd
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .direction = CKFlexboxDirectionColumn,
     .justifyContent = CKFlexboxJustifyContentCenter,
     .alignItems = CKFlexboxAlignItemsEnd,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}], 0},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,70}], 20},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,90}], 30},
   }];
  static CKSizeRange kExactSize = {{300, 300}, {300, 300}};
  CKSnapshotVerifyComponent(c, kExactSize, nil);
}

- (void)testAlignedCenter
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .direction = CKFlexboxDirectionColumn,
     .justifyContent = CKFlexboxJustifyContentCenter,
     .alignItems = CKFlexboxAlignItemsCenter,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}], 0},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,70}], 20},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,90}], 30},
   }];
  static CKSizeRange kExactSize = {{300, 300}, {300, 300}};
  CKSnapshotVerifyComponent(c, kExactSize, nil);
}

- (void)testAlignedStretchCrossSizing
{
  // Althought this test looks odd with deep yoga trees on, this
  // will actually never happen in real life, because the top yoga
  // node is always restricted to the size of the container
  // (UITableViewCell or UIScreen) so the width for the topmost node will
  // always be exact. To see the real world test case, refer to the
  // `testAlignedStretchCrossSizingWithFixedParentWidth`
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .direction = CKFlexboxDirectionColumn,
     .alignItems = CKFlexboxAlignItemsStretch,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}}
                          size:{.width = RCRelativeDimension::Percent(1.0), .height = 50}]},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150, 150}], 20},
   }];
  static CKSizeRange kVariableSize = {{100, 100}, {200, 200}};

  // all children should be 150px wide
  CKSnapshotVerifyComponent(c, kVariableSize, nil);
}

- (void)testAlignedStretchCrossSizingWithFixedParentWidth
{
  const auto c =
  CK::FlexboxComponentBuilder()
    .viewClass([UIView class])
    .backgroundColor([UIColor grayColor])
    .direction(CKFlexboxDirectionColumn)
    .useDeepYogaTrees(_useDeepYogaTrees)
    .minWidth(100)
    .maxWidth(200)
    .child(CK::ComponentBuilder()
           .viewClass([UIView class])
           .backgroundColor([UIColor redColor])
           .width(RCRelativeDimension::Percent(1.0))
           .height(50)
           .build())
    .child(CK::ComponentBuilder()
           .viewClass([UIView class])
           .backgroundColor([UIColor greenColor])
           .width(150)
           .height(150)
           .build())
      .marginTop(20)
    .build();

  const CKSizeRange kSize = {{200, 0}, {200, INFINITY}};

  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testAlignedStretchNoChildExceedsMin
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .direction = CKFlexboxDirectionColumn,
     .justifyContent = CKFlexboxJustifyContentCenter,
     .alignItems = CKFlexboxAlignItemsStretch,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}], 0},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,70}], 20},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,90}], 30},
   }];
  static CKSizeRange kVariableSize = {{200, 200}, {300, 300}};

  // all children should be 200px wide
  CKSnapshotVerifyComponent(c, kVariableSize, nil);
}

- (void)testAlignedStretchOneChildExceedsMin
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .direction = CKFlexboxDirectionColumn,
     .justifyContent = CKFlexboxJustifyContentCenter,
     .alignItems = CKFlexboxAlignItemsStretch,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}], 0},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,70}], 20},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,90}], 30},
   }];
  static CKSizeRange kVariableSize = {{50, 50}, {300, 300}};

  // all children should be 150px wide
  CKSnapshotVerifyComponent(c, kVariableSize, nil);
}

- (void)testFixedFlexBasisAppliedWhenFlexingItems
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}],
       .flexGrow = 1,
       .flexBasis = 10
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{150,150}],
       .flexGrow = 1,
       .flexBasis = 10,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,50}],
       .flexGrow = 1,
       .flexBasis = 10,
     },
   }];

  // width 300px; height 0-150px.
  static CKSizeRange kUnderflowSize = {{300, 0}, {300, 150}};
  CKSnapshotVerifyComponent(c, kUnderflowSize, @"underflow");

  // width 200px; height 0-150px.
  static CKSizeRange kOverflowSize = {{200, 0}, {200, 150}};
  CKSnapshotVerifyComponent(c, kOverflowSize, @"overflow");
}

- (void)testPercentageFlexBasisResolvesAgainstParentSize
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}],
       .flexGrow = 1,
       // This should override the intrinsic size of 50pts and instead compute to 50% = 100pts.
       // The result should be that the red box is twice as wide as the blue and gree boxes after flexing.
       .flexBasis = RCRelativeDimension::Percent(0.5)
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,50}],
       .flexGrow = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,50}],
       .flexGrow = 1,
     },
   }];

  static CKSizeRange kSize = {{200, 0}, {200, INFINITY}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testFixedFlexBasisOverridesIntrinsicSizeForNonFlexingChildren
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}],
       .flexBasis = 20
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{150,150}],
       .flexBasis = 20,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,50}],
       .flexBasis = 20,
     },
   }];

  static CKSizeRange kSize = {{300, 0}, {300, 150}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testCrossAxisStretchingOccursAfterMainAxisFlexing
{
  // If cross axis stretching occurred *before* flexing, then the blue child would be stretched to 3000 points tall.
  // Instead it should be stretched to 300 points tall, matching the red child and not overlapping the green inset.
  CKComponent *c =
  CK::InsetComponentBuilder()
  .viewClass(UIView.class)
  .backgroundColor(UIColor.greenColor)
  .insets({10, 10, 10, 10})
  .component(
   [CKFlexboxComponent
    newWithView:{}
    size:{}
    style:{
      .direction = CKFlexboxDirectionRow,
      .alignItems = CKFlexboxAlignItemsStretch,
      .useDeepYogaTrees = _useDeepYogaTrees,
    }
    children:{
      {
        [CKComponent
         newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}}
         size:{.width = 10}],
      },
      {
        [CKComponent
         newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}}
         size:{500, 500}],
        .flexGrow = 1,
        .flexShrink = 1,
      },
    }]
  ).build();

  static CKSizeRange kSize = {{300, 0}, {300, INFINITY}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testPositiveViolationIsDistributedEqually
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}],
       .flexGrow = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,50}],
       .flexGrow = 0,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,50}],
       .flexGrow = 1,
     },
   }];
  // In this scenario a width of 350 results in a positive violation of 200.
  // Due to each flexible child component specifying a flex grow factor of 1 the violation will be distributed evenly.
  static CKSizeRange kSize = {{350, 350}, {350, 350}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testPositiveViolationIsDistributedEquallyWithArbitraryFloats
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}],
       .flexGrow = 0.5,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,50}],
       .flexGrow = 0,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,50}],
       .flexGrow = 0.5,
     },
   }];
  // In this scenario a width of 350 results in a positive violation of 200.
  // Due to each flexible child component specifying a flex grow factor of 0.5 the violation will be distributed evenly.
  static CKSizeRange kSize = {{350, 350}, {350, 350}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testPositiveViolationIsDistributedProportionally
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}],
       .flexGrow = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,50}],
       .flexGrow = 2,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,50}],
       .flexGrow = 1,
     },
   }];
  // In this scenario a width of 350 results in a positive violation of 200.
  // The first and third child components specify a flex grow factor of 1 and will flex by 50.
  // The second child component specifies a flex grow factor of 2 and will flex by 100.
  static CKSizeRange kSize = {{350, 350}, {350, 350}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testPositiveViolationIsDistributedProportionallyWithArbitraryFloats
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}],
       .flexGrow = 0.25,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,50}],
       .flexGrow = 0.50,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,50}],
       .flexGrow = 0.25,
     },
   }];
  // In this scenario a width of 350 results in a positive violation of 200.
  // The first and third child components specify a flex grow factor of 0.25 and will flex by 50.
  // The second child component specifies a flex grow factor of 0.25 and will flex by 100.
  static CKSizeRange kSize = {{350, 350}, {350, 350}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testPositiveViolationIsDistributedEquallyAmongMixedChildren
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}],
       .flexShrink = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,50}],
       .flexGrow = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,50}],
       .flexShrink = 0,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor yellowColor]}}} size:{50,50}],
       .flexGrow = 1,
     },
   }];
  // In this scenario a width of 400 results in a positive violation of 200.
  // The first and third child components specify a flex shrink factor of 1 and 0, respectively. They won't flex.
  // The second and fourth child components specify a flex grow factor of 1 and will flex by 100.
  static CKSizeRange kSize = {{400, 400}, {400, 400}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testPositiveViolationIsDistributedEquallyAmongMixedChildrenWithArbitraryFloats
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}],
       .flexShrink = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,50}],
       .flexGrow = 0.5,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,50}],
       .flexShrink = 0,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor yellowColor]}}} size:{50,50}],
       .flexGrow = 0.5,
     },
   }];
  // In this scenario a width of 400 results in a positive violation of 200.
  // The first and third child components specify a flex shrink factor of 1 and 0, respectively. They won't flex.
  // The second and fourth child components specify a flex grow factor of 0.5 and will flex by 100.
  static CKSizeRange kSize = {{400, 400}, {400, 400}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testPositiveViolationIsDistributedProportionallyAmongMixedChildren
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}],
       .flexShrink = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,50}],
       .flexGrow = 3,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,50}],
       .flexShrink = 0,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor yellowColor]}}} size:{50,50}],
       .flexGrow = 1,
     },
   }];
  // In this scenario a width of 400 results in a positive violation of 200.
  // The first and third child components specify a flex shrink factor of 1 and 0, respectively. They won't flex.
  // The second child component specifies a flex grow factor of 3 and will flex by 150.
  // The fourth child component specifies a flex grow factor of 1 and will flex by 50.
  static CKSizeRange kSize = {{400, 400}, {400, 400}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testPositiveViolationIsDistributedProportionallyAmongMixedChildrenWithArbitraryFloats
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}],
       .flexShrink = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,50}],
       .flexGrow = 0.75,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,50}],
       .flexShrink = 0,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor yellowColor]}}} size:{50,50}],
       .flexGrow = 0.25,
     },
   }];
  // In this scenario a width of 400 results in a positive violation of 200.
  // The first and third child components specify a flex shrink factor of 1 and 0, respectively. They won't flex.
  // The second child component specifies a flex grow factor of 0.75 and will flex by 150.
  // The fourth child component specifies a flex grow factor of 0.25 and will flex by 50.
  static CKSizeRange kSize = {{400, 400}, {400, 400}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testRemainingViolationIsAppliedProperlyToFirstFlexibleChild
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,25}],
       .flexGrow = 0,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,0}],
       .flexGrow = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,100}],
       .flexGrow = 1,
     },
   }];
  static CKSizeRange kSize = {{300, 300}, {300, 300}};
  // In this scenario a width of 300 results in a positive violation of 175.
  // The second and third child components specify a flex grow factor of 1 and will flex by 88 and 87, respectively.
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testRemainingViolationIsAppliedProperlyToFirstFlexibleChildWithArbitraryFloats
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,25}],
       .flexGrow = 0,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,0}],
       .flexGrow = 0.5,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,100}],
       .flexGrow = 0.5,
     },
   }];
  static CKSizeRange kSize = {{300, 300}, {300, 300}};
  // In this scenario a width of 300 results in a positive violation of 175.
  // The second and third child components specify a flex grow factor of 0.5 and will flex by 88 and 87, respectively.
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testNegativeViolationIsDistributedBasedOnSize
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{300,50}],
       .flexShrink = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,50}],
       .flexShrink = 0,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{200,50}],
       .flexShrink = 1,
     },
   }];
  // In this scenario a width of 400 results in a negative violation of 200.
  // The first and third child components specify a flex shrink factor of 1 and will flex by -120 and -80, respectively.
  static CKSizeRange kSize = {{400, 400}, {400, 400}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testNegativeViolationIsDistributedBasedOnSizeWithArbitraryFloats
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{300,50}],
       .flexShrink = 0.5,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,50}],
       .flexShrink = 0,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{200,50}],
       .flexShrink = 0.5,
     },
   }];
  // In this scenario a width of 400 results in a negative violation of 200.
  // The first and third child components specify a flex shrink factor of 0.5 and will flex by -120 and -80, respectively.
  static CKSizeRange kSize = {{400, 400}, {400, 400}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testNegativeViolationIsDistributedBasedOnSizeAndFlexFactor
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,300}],
       .flexShrink = 2,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,100}],
       .flexShrink = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,200}],
       .flexShrink = 2,
     },
   }];
  // In this scenario a width of 400 results in a negative violation of 200.
  // The first and third child components specify a flex shrink factor of 2 and will flex by -109 and -72, respectively.
  // The second child component specifies a flex shrink factor of 1 and will flex by -18.
  static CKSizeRange kSize = {{400, 400}, {400, 400}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorWithArbitraryFloats
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,300}],
       .flexShrink = 0.4,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,100}],
       .flexShrink = 0.2,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,200}],
       .flexShrink = 0.4,
     },
   }];
  // In this scenario a width of 400 results in a negative violation of 200.
  // The first and third child components specify a flex shrink factor of 0.4 and will flex by -109 and -72, respectively.
  // The second child component specifies a flex shrink factor of 0.2 and will flex by -18.
  static CKSizeRange kSize = {{400, 400}, {400, 400}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testNegativeViolationIsDistributedBasedOnSizeAmongMixedChildrenChildren
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{150,50}],
       .flexGrow = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{150,50}],
       .flexShrink = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,50}],
       .flexGrow = 0,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor yellowColor]}}} size:{150,50}],
       .flexShrink = 1,
     },
   }];
  // In this scenario a width of 400 results in a negative violation of 200.
  // The first and third child components specify a flex grow factor of 1 and 0, respectively. They won't flex.
  // The second and fourth child components specify a flex shrink factor of 1 and will flex by -100.
  static CKSizeRange kSize = {{400, 400}, {400, 400}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testNegativeViolationIsDistributedBasedOnSizeAmongMixedChildrenWithArbitraryFloats
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{150,50}],
       .flexGrow = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{150,50}],
       .flexShrink = 0.5,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{150,50}],
       .flexGrow = 0,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor yellowColor]}}} size:{150,50}],
       .flexShrink = 0.5,
     },
   }];
  // In this scenario a width of 400 results in a negative violation of 200.
  // The first and third child components specify a flex grow factor of 1 and 0, respectively. They won't flex.
  // The second and fourth child components specify a flex shrink factor of 0.5 and will flex by -100.
  static CKSizeRange kSize = {{400, 400}, {400, 400}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorAmongMixedChildren
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,150}],
       .flexGrow = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,100}],
       .flexShrink = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,150}],
       .flexGrow = 0,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor yellowColor]}}} size:{50,200}],
       .flexShrink = 3,
     },
   }];
  // In this scenario a width of 400 results in a negative violation of 200.
  // The first and third child components specify a flex grow factor of 1 and 0, respectively. They won't flex.
  // The second child component specifies a flex shrink factor of 1 and will flex by -28.
  // The fourth child component specifies a flex shrink factor of 3 and will flex by -171.
  static CKSizeRange kSize = {{400, 400}, {400, 400}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorAmongMixedChildrenArbitraryFloats
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionColumn,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,150}],
       .flexGrow = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,100}],
       .flexShrink = 0.25,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,150}],
       .flexGrow = 0,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor yellowColor]}}} size:{50,200}],
       .flexShrink = 0.75,
     },
   }];
  // In this scenario a width of 400 results in a negative violation of 200.
  // The first and third child components specify a flex grow factor of 1 and 0, respectively. They won't flex.
  // The second child component specifies a flex shrink factor of 0.25 and will flex by -28.
  // The fourth child component specifies a flex shrink factor of 0.75 and will flex by -171.
  static CKSizeRange kSize = {{400, 400}, {400, 400}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorDoesNotShrinkToZero
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{300,50}],
       .flexShrink = 1,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,50}],
       .flexShrink = 2,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{200,50}],
       .flexShrink = 1,
     },
   }];
  // In this scenario a width of 400 results in a negative violation of 200.
  // The first and third child components specify a flex shrink factor of 1 and will flex by 50.
  // The second child component specifies a flex shrink factor of 2 and will flex by -57. It will have a width of 43.
  static CKSizeRange kSize = {{400, 400}, {400, 400}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorDoesNotShrinkToZeroWithArbitraryFloats
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{300,50}],
       .flexShrink = 0.25,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,50}],
       .flexShrink = 0.50,
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{200,50}],
       .flexShrink = 0.25,
     },
   }];
  // In this scenario a width of 400 results in a negative violation of 200.
  // The first and third child components specify a flex shrink factor of 0.25 and will flex by 50.
  // The second child component specifies a flex shrink factor of 0.50 and will flex by -57. It will have a width of 43.
  static CKSizeRange kSize = {{400, 400}, {400, 400}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testNestedLayoutStretchDoesNotViolateWidth
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:{
     [UIView class],
     {
       {@selector(setBackgroundColor:), [UIColor blueColor]}
     }
   }
   size:{
     .width = 100,
     .height = 100
   }
   style:{
     .direction = CKFlexboxDirectionColumn,
     .alignItems = CKFlexboxAlignItemsStretch,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent
        newWithView:{
          [UIView class],
          {
            {@selector(setBackgroundColor:), [UIColor redColor]}
          }
        } size:{
          .width = 50,
          .height = 50
        }]
     }
   }];
  static CKSizeRange kSize = {{0, 0}, {300, INFINITY}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testSimultaneousFlexGrowAndAlignStretch
{
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{.height = 100}
   style:{
     .direction = CKFlexboxDirectionRow,
     // This should make each child stretch to the full height of 100pts:
     .alignItems = CKFlexboxAlignItemsStretch,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       // CKCompositeComponent is used just to verify that CKFlexboxComponent is actually
       // laying out each child at the correct size, not just setting RCLayout.size.
       [CKCompositeComponent
        newWithComponent:
        [CKComponent
         newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}}
         size:{}]],
       .flexGrow = 1,
     },
     {
       [CKCompositeComponent
        newWithComponent:
        [CKComponent
         newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}}
         size:{}]],
       .flexGrow = 1,
     },
     {
       [CKCompositeComponent
        newWithComponent:
        [CKComponent
         newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}}
         size:{}]],
       .flexGrow = 1,
     },
   }];
  static CKSizeRange kSize = {{400, 0}, {400, INFINITY}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

# pragma mark - UI Docs

/**
 * @uidocs CKFlexboxComponent
 * @uidocs_title Start alignment
 */
- (void)test_uidocs_alignedStart
{
  static CKSizeRange kSize = {{0,0}, {INFINITY, INFINITY}};
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .direction = CKFlexboxDirectionColumn,
     .justifyContent = CKFlexboxJustifyContentCenter,
     .alignItems = CKFlexboxAlignItemsStart,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{25,25}], 0},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{75,25}], 10},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,25}], 10},
   }];
  CKSnapshotVerifyComponent(c, kSize, nil);
}

/**
 * @uidocs CKFlexboxComponent
 * @uidocs_title Center alignment
 */
- (void)test_uidocs_alignedCenter
{
  static CKSizeRange kSize = {{0,0}, {INFINITY, INFINITY}};
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .direction = CKFlexboxDirectionColumn,
     .justifyContent = CKFlexboxJustifyContentCenter,
     .alignItems = CKFlexboxAlignItemsCenter,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{25,25}], 0},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{75,25}], 10},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,25}], 10},
   }];
  CKSnapshotVerifyComponent(c, kSize, nil);
}

/**
 * @uidocs CKFlexboxComponent
 * @uidocs_title End alignment
 */
- (void)test_uidocs_alignedEnd
{
  static CKSizeRange kSize = {{0,0}, {INFINITY, INFINITY}};
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .direction = CKFlexboxDirectionColumn,
     .justifyContent = CKFlexboxJustifyContentCenter,
     .alignItems = CKFlexboxAlignItemsEnd,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{25,25}], 0},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{75,25}], 10},
     {[CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{100,25}], 10},
   }];
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testAspectRatioOneToOne {
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{10,5}],
       .aspectRatio = 1
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{25,5}],
       .aspectRatio = 1
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{40,5}],
       .aspectRatio = 1
     },
   }];

  static CKSizeRange kSize = {{75, 75}, {75, 75}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testAspectRatioTwoToOne {
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{10,5}],
       .aspectRatio = 2
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{25,5}],
       .aspectRatio = 2
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{40,5}],
       .aspectRatio = 2
     },
   }];

  static CKSizeRange kSize = {{75, 50}, {75, 50}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testAspectRatioOneToTwo {
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{10,5}],
       .aspectRatio = 0.5
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{25,5}],
       .aspectRatio = 0.5
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{40,5}],
       .aspectRatio = 0.5
     },
   }];

  static CKSizeRange kSize = {{75, 100}, {75, 100}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testNegativeAspectRatioOneToOne {
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{10,5}],
       .aspectRatio = -1
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{25,5}],
       .aspectRatio = -1
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{40,5}],
       .aspectRatio = -1
     },
   }];

  static CKSizeRange kSize = {{75, 75}, {75, 75}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testMinAspectRatio {
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{10,5}],
       .aspectRatio = FLT_EPSILON + FLT_MIN_EXP
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{25,5}],
       .aspectRatio = FLT_EPSILON + FLT_MIN_EXP
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{40,5}],
       .aspectRatio = FLT_EPSILON + FLT_MIN_EXP
     },
   }];

  static CKSizeRange kSize = {{75, 75}, {75, 75}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

- (void)testDifferentAspectRatioPerChild {
  CKComponent *c =
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{10,5}],
       .aspectRatio = 0.5
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{25,5}],
       .aspectRatio = 2
     },
     {
       [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{40,5}],
       .aspectRatio = 1
     },
   }];

  static CKSizeRange kSize = {{75, 75}, {75, 75}};
  CKSnapshotVerifyComponent(c, kSize, nil);
}

static CKFlexboxComponentChild flexChild(CKComponent *c, CGFloat flexFactor)
{
  return {c, .flexGrow = flexFactor, .flexShrink = flexFactor};
}

- (CKComponent *)_layoutWithJustify:(CKFlexboxJustifyContent)justify flexFactor:(NSInteger)flexFactor
{
  return
  [CKFlexboxComponent
   newWithView:kWhiteBackgroundView
   size:{}
   style:{
     .alignItems = CKFlexboxAlignItemsStart,
     .direction = CKFlexboxDirectionRow,
     .justifyContent = justify,
     .useDeepYogaTrees = _useDeepYogaTrees,
   }
   children:{
     flexChild([CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{50,50}], flexFactor),
     flexChild([CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor blueColor]}}} size:{50,50}], flexFactor),
     flexChild([CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor greenColor]}}} size:{50,50}], flexFactor),
   }];
}

@end

@interface CKFlexboxComponentWithDeepYogaTreeSnapshotTests : CKFlexboxComponentSnapshotTests
@end

@implementation CKFlexboxComponentWithDeepYogaTreeSnapshotTests

- (void)setUp
{
  [super setUp];
  self.useDeepYogaTrees = YES;
}

@end
