Skip to content

Commit 934d52b

Browse files
authored
Merge branch 'main' into fix/gentest-types-casing
2 parents baa0f85 + d382d33 commit 934d52b

6 files changed

Lines changed: 310 additions & 5 deletions

File tree

javascript/src/wrapAssembly.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ type Layout = {
3535
bottom: number;
3636
width: number;
3737
height: number;
38+
hadOverflow: boolean;
3839
};
3940

4041
type Size = {
@@ -85,6 +86,7 @@ export type Node = {
8586
getChildCount(): number;
8687
getComputedBorder(edge: Edge): number;
8788
getComputedBottom(): number;
89+
getComputedHadOverflow(): boolean;
8890
getComputedHeight(): number;
8991
getComputedLayout(): Layout;
9092
getComputedLeft(): number;
@@ -965,6 +967,10 @@ export default function wrapAssembly(lib: any): Yoga {
965967
return lib._YGNodeLayoutGetHeight(this._ptr);
966968
}
967969

970+
getComputedHadOverflow(): boolean {
971+
return !!lib._YGNodeLayoutGetHadOverflow(this._ptr);
972+
}
973+
968974
getComputedLayout(): Layout {
969975
return {
970976
left: lib._YGNodeLayoutGetLeft(this._ptr),
@@ -973,6 +979,7 @@ export default function wrapAssembly(lib: any): Yoga {
973979
bottom: lib._YGNodeLayoutGetBottom(this._ptr),
974980
width: lib._YGNodeLayoutGetWidth(this._ptr),
975981
height: lib._YGNodeLayoutGetHeight(this._ptr),
982+
hadOverflow: !!lib._YGNodeLayoutGetHadOverflow(this._ptr),
976983
};
977984
}
978985

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import Yoga from 'yoga-layout';
9+
import {Direction, FlexDirection, Wrap} from 'yoga-layout';
10+
11+
test('children_overflow_no_wrap_and_no_flex_children', () => {
12+
const root = Yoga.Node.create();
13+
root.setWidth(200);
14+
root.setHeight(100);
15+
root.setFlexDirection(FlexDirection.Column);
16+
root.setFlexWrap(Wrap.NoWrap);
17+
18+
const child0 = Yoga.Node.create();
19+
child0.setWidth(80);
20+
child0.setHeight(40);
21+
child0.setMargin(Yoga.EDGE_TOP, 10);
22+
child0.setMargin(Yoga.EDGE_BOTTOM, 15);
23+
root.insertChild(child0, 0);
24+
25+
const child1 = Yoga.Node.create();
26+
child1.setWidth(80);
27+
child1.setHeight(40);
28+
child1.setMargin(Yoga.EDGE_BOTTOM, 5);
29+
root.insertChild(child1, 1);
30+
31+
root.calculateLayout(200, 100, Direction.LTR);
32+
33+
expect(root.getComputedHadOverflow()).toBe(true);
34+
});
35+
36+
test('no_overflow_no_wrap_and_flex_children', () => {
37+
const root = Yoga.Node.create();
38+
root.setWidth(200);
39+
root.setHeight(100);
40+
root.setFlexDirection(FlexDirection.Column);
41+
root.setFlexWrap(Wrap.NoWrap);
42+
43+
const child0 = Yoga.Node.create();
44+
child0.setWidth(80);
45+
child0.setHeight(40);
46+
child0.setMargin(Yoga.EDGE_TOP, 10);
47+
child0.setMargin(Yoga.EDGE_BOTTOM, 10);
48+
root.insertChild(child0, 0);
49+
50+
const child1 = Yoga.Node.create();
51+
child1.setWidth(80);
52+
child1.setHeight(40);
53+
child1.setMargin(Yoga.EDGE_BOTTOM, 5);
54+
child1.setFlexShrink(1);
55+
root.insertChild(child1, 1);
56+
57+
root.calculateLayout(200, 100, Direction.LTR);
58+
59+
expect(root.getComputedHadOverflow()).toBe(false);
60+
});
61+
62+
test('hadOverflow_gets_reset_if_no_longer_valid', () => {
63+
const root = Yoga.Node.create();
64+
root.setWidth(200);
65+
root.setHeight(100);
66+
root.setFlexDirection(FlexDirection.Column);
67+
root.setFlexWrap(Wrap.NoWrap);
68+
69+
const child0 = Yoga.Node.create();
70+
child0.setWidth(80);
71+
child0.setHeight(40);
72+
child0.setMargin(Yoga.EDGE_TOP, 10);
73+
child0.setMargin(Yoga.EDGE_BOTTOM, 10);
74+
root.insertChild(child0, 0);
75+
76+
const child1 = Yoga.Node.create();
77+
child1.setWidth(80);
78+
child1.setHeight(40);
79+
child1.setMargin(Yoga.EDGE_BOTTOM, 5);
80+
root.insertChild(child1, 1);
81+
82+
root.calculateLayout(200, 100, Direction.LTR);
83+
expect(root.getComputedHadOverflow()).toBe(true);
84+
85+
child1.setFlexShrink(1);
86+
root.calculateLayout(200, 100, Direction.LTR);
87+
expect(root.getComputedHadOverflow()).toBe(false);
88+
});
89+
90+
test('hadOverflow_included_in_getComputedLayout', () => {
91+
const root = Yoga.Node.create();
92+
root.setWidth(200);
93+
root.setHeight(100);
94+
root.setFlexDirection(FlexDirection.Column);
95+
96+
const child0 = Yoga.Node.create();
97+
child0.setWidth(80);
98+
child0.setHeight(80);
99+
root.insertChild(child0, 0);
100+
101+
const child1 = Yoga.Node.create();
102+
child1.setWidth(80);
103+
child1.setHeight(80);
104+
root.insertChild(child1, 1);
105+
106+
root.calculateLayout(200, 100, Direction.LTR);
107+
108+
const layout = root.getComputedLayout();
109+
expect(layout.hadOverflow).toBe(true);
110+
});

tests/YGConfigTest.cpp

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include <functional>
1414
#include <memory>
15+
#include <stdexcept>
1516

1617
using namespace facebook;
1718

@@ -67,3 +68,165 @@ void ConfigCloningTest::TearDown() {
6768
}
6869

6970
yoga::Node ConfigCloningTest::clonedNode = {};
71+
72+
TEST(YogaTest, config_point_scale_factor_negative_throws) {
73+
YGConfigRef config = YGConfigNew();
74+
75+
// Zero is explicitly allowed per the API contract
76+
YGConfigSetPointScaleFactor(config, 0.0f);
77+
ASSERT_EQ(YGConfigGetPointScaleFactor(config), 0.0f);
78+
79+
// Negative values should trigger a fatal assertion (throws std::logic_error)
80+
ASSERT_THROW(YGConfigSetPointScaleFactor(config, -1.0f), std::logic_error);
81+
82+
// Verify the value was not changed after the failed set
83+
ASSERT_EQ(YGConfigGetPointScaleFactor(config), 0.0f);
84+
85+
YGConfigFree(config);
86+
}
87+
88+
TEST(YogaTest, config_set_logger_null_resets_to_default_logger) {
89+
YGConfigRef config = YGConfigNew();
90+
91+
// Track whether our custom logger was called
92+
bool customLoggerCalled = false;
93+
YGConfigSetContext(config, &customLoggerCalled);
94+
95+
YGLogger customLogger = [](YGConfigConstRef cfg,
96+
YGNodeConstRef /*node*/,
97+
YGLogLevel /*level*/,
98+
const char* /*format*/,
99+
va_list /*args*/) -> int {
100+
bool* called = static_cast<bool*>(YGConfigGetContext(cfg));
101+
*called = true;
102+
return 0;
103+
};
104+
105+
// Set custom logger and verify it's invoked via layout warning
106+
YGConfigSetLogger(config, customLogger);
107+
YGNodeRef node = YGNodeNewWithConfig(config);
108+
YGNodeCalculateLayout(node, YGUndefined, YGUndefined, YGDirectionLTR);
109+
// Trigger a log by setting a negative point scale factor (which logs before
110+
// throwing)
111+
customLoggerCalled = false;
112+
ASSERT_THROW(YGConfigSetPointScaleFactor(config, -1.0f), std::logic_error);
113+
ASSERT_TRUE(customLoggerCalled);
114+
115+
// Reset logger to null — should fall back to default logger
116+
YGConfigSetLogger(config, nullptr);
117+
customLoggerCalled = false;
118+
ASSERT_THROW(YGConfigSetPointScaleFactor(config, -2.0f), std::logic_error);
119+
// Custom logger should NOT have been called since we reset to default
120+
ASSERT_FALSE(customLoggerCalled);
121+
122+
YGNodeFree(node);
123+
YGConfigFree(config);
124+
}
125+
126+
TEST(YogaTest, config_version_increments_only_on_actual_changes) {
127+
auto* config = static_cast<yoga::Config*>(YGConfigNew());
128+
129+
uint32_t initialVersion = config->getVersion();
130+
131+
// Changing point scale factor should increment version
132+
config->setPointScaleFactor(2.0f);
133+
ASSERT_EQ(config->getVersion(), initialVersion + 1);
134+
135+
// Setting the same value again should NOT increment version
136+
config->setPointScaleFactor(2.0f);
137+
ASSERT_EQ(config->getVersion(), initialVersion + 1);
138+
139+
// Changing errata should increment version
140+
config->setErrata(yoga::Errata::StretchFlexBasis);
141+
ASSERT_EQ(config->getVersion(), initialVersion + 2);
142+
143+
// Setting the same errata again should NOT increment version
144+
config->setErrata(yoga::Errata::StretchFlexBasis);
145+
ASSERT_EQ(config->getVersion(), initialVersion + 2);
146+
147+
// Enabling an experimental feature should increment version
148+
config->setExperimentalFeatureEnabled(
149+
yoga::ExperimentalFeature::WebFlexBasis, true);
150+
ASSERT_EQ(config->getVersion(), initialVersion + 3);
151+
152+
// Enabling the same feature again should NOT increment version
153+
config->setExperimentalFeatureEnabled(
154+
yoga::ExperimentalFeature::WebFlexBasis, true);
155+
ASSERT_EQ(config->getVersion(), initialVersion + 3);
156+
157+
YGConfigFree(config);
158+
}
159+
160+
TEST(YogaTest, config_update_invalidates_layout_detects_each_property) {
161+
auto* config1 = static_cast<yoga::Config*>(YGConfigNew());
162+
auto* config2 = static_cast<yoga::Config*>(YGConfigNew());
163+
164+
// Two identical configs should not invalidate layout
165+
ASSERT_FALSE(yoga::configUpdateInvalidatesLayout(*config1, *config2));
166+
167+
// Changing errata on one config should invalidate
168+
config2->setErrata(yoga::Errata::StretchFlexBasis);
169+
ASSERT_TRUE(yoga::configUpdateInvalidatesLayout(*config1, *config2));
170+
171+
// Make them match again
172+
config1->setErrata(yoga::Errata::StretchFlexBasis);
173+
ASSERT_FALSE(yoga::configUpdateInvalidatesLayout(*config1, *config2));
174+
175+
// Changing point scale factor should invalidate
176+
config2->setPointScaleFactor(3.0f);
177+
ASSERT_TRUE(yoga::configUpdateInvalidatesLayout(*config1, *config2));
178+
179+
// Make them match again
180+
config1->setPointScaleFactor(3.0f);
181+
ASSERT_FALSE(yoga::configUpdateInvalidatesLayout(*config1, *config2));
182+
183+
// Changing useWebDefaults should invalidate
184+
config2->setUseWebDefaults(true);
185+
ASSERT_TRUE(yoga::configUpdateInvalidatesLayout(*config1, *config2));
186+
187+
// Make them match again
188+
config1->setUseWebDefaults(true);
189+
ASSERT_FALSE(yoga::configUpdateInvalidatesLayout(*config1, *config2));
190+
191+
// Changing experimental features should invalidate
192+
config2->setExperimentalFeatureEnabled(
193+
yoga::ExperimentalFeature::WebFlexBasis, true);
194+
ASSERT_TRUE(yoga::configUpdateInvalidatesLayout(*config1, *config2));
195+
196+
YGConfigFree(config1);
197+
YGConfigFree(config2);
198+
}
199+
200+
TEST(YogaTest, config_errata_bitmask_add_remove_operations) {
201+
auto* config = static_cast<yoga::Config*>(YGConfigNew());
202+
203+
// Initially no errata
204+
ASSERT_FALSE(config->hasErrata(yoga::Errata::StretchFlexBasis));
205+
ASSERT_FALSE(config->hasErrata(
206+
yoga::Errata::AbsolutePositionWithoutInsetsExcludesPadding));
207+
208+
// Add one errata flag
209+
config->addErrata(yoga::Errata::StretchFlexBasis);
210+
ASSERT_TRUE(config->hasErrata(yoga::Errata::StretchFlexBasis));
211+
ASSERT_FALSE(config->hasErrata(
212+
yoga::Errata::AbsolutePositionWithoutInsetsExcludesPadding));
213+
214+
// Add another errata flag — first should still be set
215+
config->addErrata(yoga::Errata::AbsolutePositionWithoutInsetsExcludesPadding);
216+
ASSERT_TRUE(config->hasErrata(yoga::Errata::StretchFlexBasis));
217+
ASSERT_TRUE(config->hasErrata(
218+
yoga::Errata::AbsolutePositionWithoutInsetsExcludesPadding));
219+
220+
// Remove only the first flag — second should remain
221+
config->removeErrata(yoga::Errata::StretchFlexBasis);
222+
ASSERT_FALSE(config->hasErrata(yoga::Errata::StretchFlexBasis));
223+
ASSERT_TRUE(config->hasErrata(
224+
yoga::Errata::AbsolutePositionWithoutInsetsExcludesPadding));
225+
226+
// Remove the second flag
227+
config->removeErrata(
228+
yoga::Errata::AbsolutePositionWithoutInsetsExcludesPadding);
229+
ASSERT_EQ(config->getErrata(), yoga::Errata::None);
230+
231+
YGConfigFree(config);
232+
}

website/docs/advanced/external-layout-systems.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Measure functions in the C/C++ APIs are represented as C function pointers and d
2323
```cpp
2424
Widget widget{};
2525

26-
YGNodeSetContext(node, &w);
26+
YGNodeSetContext(node, &widget);
2727
YGNodeSetMeasureFunc(node, &measureWidget);
2828
```
2929

website/docs/advanced/incremental-layout.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ void applyLayout(YogaNode node) {
6060
<TabItem value="js" label="JavaScript">
6161

6262
```javascript
63-
void applyLayout(node) {
63+
function applyLayout(node) {
6464
if (!node.hasNewLayout()) {
6565
return;
6666
}

yoga/style/Style.h

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -661,10 +661,10 @@ class YG_EXPORT Style {
661661
lengthsEqual(padding_, pool_, other.padding_, other.pool_) &&
662662
lengthsEqual(border_, pool_, other.border_, other.pool_) &&
663663
lengthsEqual(gap_, pool_, other.gap_, other.pool_) &&
664-
lengthsEqual(dimensions_, pool_, other.dimensions_, other.pool_) &&
665-
lengthsEqual(
664+
sizeLengthsEqual(dimensions_, pool_, other.dimensions_, other.pool_) &&
665+
sizeLengthsEqual(
666666
minDimensions_, pool_, other.minDimensions_, other.pool_) &&
667-
lengthsEqual(
667+
sizeLengthsEqual(
668668
maxDimensions_, pool_, other.maxDimensions_, other.pool_) &&
669669
numbersEqual(aspectRatio_, pool_, other.aspectRatio_, other.pool_) &&
670670
gridTemplateColumns_ == other.gridTemplateColumns_ &&
@@ -716,6 +716,31 @@ class YG_EXPORT Style {
716716
});
717717
}
718718

719+
static inline bool sizeLengthsEqual(
720+
const StyleValueHandle& lhsHandle,
721+
const StyleValuePool& lhsPool,
722+
const StyleValueHandle& rhsHandle,
723+
const StyleValuePool& rhsPool) {
724+
return (lhsHandle.isUndefined() && rhsHandle.isUndefined()) ||
725+
(lhsPool.getSize(lhsHandle) == rhsPool.getSize(rhsHandle));
726+
}
727+
728+
template <size_t N>
729+
static inline bool sizeLengthsEqual(
730+
const std::array<StyleValueHandle, N>& lhs,
731+
const StyleValuePool& lhsPool,
732+
const std::array<StyleValueHandle, N>& rhs,
733+
const StyleValuePool& rhsPool) {
734+
return std::equal(
735+
lhs.begin(),
736+
lhs.end(),
737+
rhs.begin(),
738+
rhs.end(),
739+
[&](const auto& lhs, const auto& rhs) {
740+
return sizeLengthsEqual(lhs, lhsPool, rhs, rhsPool);
741+
});
742+
}
743+
719744
StyleValueHandle computeColumnGap() const {
720745
if (gap_[yoga::to_underlying(Gutter::Column)].isDefined()) {
721746
return gap_[yoga::to_underlying(Gutter::Column)];

0 commit comments

Comments
 (0)