Skip to content

Commit 979dcc3

Browse files
DevmateUtgenCppGeneralFBOrg Botfacebook-github-bot
authored andcommitted
xplat/js/react-native-github/packages/react-native/ReactCommon/yoga/yoga/algorithm/Cache.cpp
Reviewed By: cortinico Differential Revision: D112072153
1 parent 6d3a8e0 commit 979dcc3

1 file changed

Lines changed: 229 additions & 0 deletions

File tree

tests/CacheTest.cpp

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
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+
#include <gtest/gtest.h>
9+
10+
#include <yoga/Yoga.h>
11+
#include <yoga/algorithm/Cache.h>
12+
#include <yoga/algorithm/SizingMode.h>
13+
#include <yoga/config/Config.h>
14+
15+
namespace facebook::yoga {
16+
17+
class CacheTest : public ::testing::Test {
18+
protected:
19+
void SetUp() override {
20+
config_ = static_cast<yoga::Config*>(YGConfigNew());
21+
}
22+
23+
void TearDown() override {
24+
YGConfigFree(config_);
25+
}
26+
27+
yoga::Config* config_ = nullptr;
28+
};
29+
30+
TEST_F(CacheTest, negativeCachedMeasurementDisablesCache) {
31+
// Cached values with defined-but-negative extents are treated as invalid;
32+
// both axes must independently invalidate the cache.
33+
const bool negativeWidth = canUseCachedMeasurement(
34+
SizingMode::StretchFit,
35+
100.0f,
36+
SizingMode::StretchFit,
37+
50.0f,
38+
SizingMode::StretchFit,
39+
100.0f,
40+
SizingMode::StretchFit,
41+
50.0f,
42+
/*lastComputedWidth=*/-1.0f,
43+
/*lastComputedHeight=*/50.0f,
44+
0.0f,
45+
0.0f,
46+
config_);
47+
EXPECT_FALSE(negativeWidth);
48+
49+
const bool negativeHeight = canUseCachedMeasurement(
50+
SizingMode::StretchFit,
51+
100.0f,
52+
SizingMode::StretchFit,
53+
50.0f,
54+
SizingMode::StretchFit,
55+
100.0f,
56+
SizingMode::StretchFit,
57+
50.0f,
58+
/*lastComputedWidth=*/100.0f,
59+
/*lastComputedHeight=*/-0.5f,
60+
0.0f,
61+
0.0f,
62+
config_);
63+
EXPECT_FALSE(negativeHeight);
64+
}
65+
66+
TEST_F(CacheTest, identicalSpecsAllowCacheReuse) {
67+
// Primary cache-hit path: identical mode + available size on both axes
68+
// means the previously computed measurement can be reused verbatim.
69+
const bool canUse = canUseCachedMeasurement(
70+
SizingMode::StretchFit,
71+
100.0f,
72+
SizingMode::FitContent,
73+
50.0f,
74+
SizingMode::StretchFit,
75+
100.0f,
76+
SizingMode::FitContent,
77+
50.0f,
78+
/*lastComputedWidth=*/95.0f,
79+
/*lastComputedHeight=*/45.0f,
80+
0.0f,
81+
0.0f,
82+
config_);
83+
84+
EXPECT_TRUE(canUse);
85+
}
86+
87+
TEST_F(CacheTest, incompatibleSpecsDisableCache) {
88+
// Previous run used MaxContent with a computed size of 95; the new run
89+
// stretches to 200. StretchFit only reuses a cached size when the new
90+
// inner extent equals the last computed size, which does not hold here,
91+
// so no compatibility branch applies.
92+
const bool canUse = canUseCachedMeasurement(
93+
SizingMode::StretchFit,
94+
200.0f,
95+
SizingMode::StretchFit,
96+
200.0f,
97+
SizingMode::MaxContent,
98+
YGUndefined,
99+
SizingMode::MaxContent,
100+
YGUndefined,
101+
/*lastComputedWidth=*/95.0f,
102+
/*lastComputedHeight=*/95.0f,
103+
0.0f,
104+
0.0f,
105+
config_);
106+
107+
EXPECT_FALSE(canUse);
108+
}
109+
110+
TEST_F(
111+
CacheTest,
112+
stretchFitMatchingComputedSizeReusesCacheAccountingForMargin) {
113+
// Under StretchFit, the requested content size is (availableWidth -
114+
// marginRow) = 90, which matches the last computed width of 90. This
115+
// hits the sizeIsExactAndMatchesOldMeasuredSize branch and also verifies
116+
// that marginRow is subtracted before the comparison (a bare 100 would
117+
// not match 90).
118+
const bool canUse = canUseCachedMeasurement(
119+
SizingMode::StretchFit,
120+
/*availableWidth=*/100.0f,
121+
SizingMode::StretchFit,
122+
/*availableHeight=*/60.0f,
123+
SizingMode::FitContent,
124+
/*lastAvailableWidth=*/90.0f,
125+
SizingMode::StretchFit,
126+
/*lastAvailableHeight=*/60.0f,
127+
/*lastComputedWidth=*/90.0f,
128+
/*lastComputedHeight=*/60.0f,
129+
/*marginRow=*/10.0f,
130+
/*marginColumn=*/0.0f,
131+
config_);
132+
133+
EXPECT_TRUE(canUse);
134+
}
135+
136+
TEST_F(CacheTest, maxContentResultStillFitsFitContentRequest) {
137+
// Cached MaxContent width of 80 fits inside a FitContent request with 120
138+
// available space, so the previous result remains valid (the max-content
139+
// size cannot overflow the looser constraint).
140+
const bool canUse = canUseCachedMeasurement(
141+
SizingMode::FitContent,
142+
/*availableWidth=*/120.0f,
143+
SizingMode::StretchFit,
144+
/*availableHeight=*/50.0f,
145+
SizingMode::MaxContent,
146+
/*lastAvailableWidth=*/YGUndefined,
147+
SizingMode::StretchFit,
148+
/*lastAvailableHeight=*/50.0f,
149+
/*lastComputedWidth=*/80.0f,
150+
/*lastComputedHeight=*/50.0f,
151+
0.0f,
152+
0.0f,
153+
config_);
154+
155+
EXPECT_TRUE(canUse);
156+
}
157+
158+
TEST_F(CacheTest, tighterFitContentReusesCacheWhenPreviousResultStillFits) {
159+
// Both runs use FitContent. The new available width (100) is stricter than
160+
// the previous one (150), but the previously computed width (80) still
161+
// fits inside the new constraint, so the cached measurement is reusable.
162+
const bool canUse = canUseCachedMeasurement(
163+
SizingMode::FitContent,
164+
/*availableWidth=*/100.0f,
165+
SizingMode::StretchFit,
166+
/*availableHeight=*/50.0f,
167+
SizingMode::FitContent,
168+
/*lastAvailableWidth=*/150.0f,
169+
SizingMode::StretchFit,
170+
/*lastAvailableHeight=*/50.0f,
171+
/*lastComputedWidth=*/80.0f,
172+
/*lastComputedHeight=*/50.0f,
173+
0.0f,
174+
0.0f,
175+
config_);
176+
177+
EXPECT_TRUE(canUse);
178+
}
179+
180+
TEST_F(
181+
CacheTest,
182+
tighterFitContentInvalidatesCacheWhenPreviousResultOverflows) {
183+
// Same shape as the previous test but the cached width (120) is larger
184+
// than the tightened constraint (100). Reusing the measurement would
185+
// silently overflow the container, so the cache must be discarded.
186+
const bool canUse = canUseCachedMeasurement(
187+
SizingMode::FitContent,
188+
/*availableWidth=*/100.0f,
189+
SizingMode::StretchFit,
190+
/*availableHeight=*/50.0f,
191+
SizingMode::FitContent,
192+
/*lastAvailableWidth=*/150.0f,
193+
SizingMode::StretchFit,
194+
/*lastAvailableHeight=*/50.0f,
195+
/*lastComputedWidth=*/120.0f,
196+
/*lastComputedHeight=*/50.0f,
197+
0.0f,
198+
0.0f,
199+
config_);
200+
201+
EXPECT_FALSE(canUse);
202+
}
203+
204+
TEST_F(CacheTest, pixelGridRoundingMakesSubPixelSpecsEquivalent) {
205+
// At a point scale factor of 2, both 10.25 and 10.5 snap to 10.5 on the
206+
// physical pixel grid. Specs that differ by less than one device pixel
207+
// must be treated as identical so we can reuse the cache; without grid
208+
// rounding, inexactEquals(10.25, 10.5) is false and the cache would miss.
209+
config_->setPointScaleFactor(2.0f);
210+
211+
const bool canUse = canUseCachedMeasurement(
212+
SizingMode::StretchFit,
213+
/*availableWidth=*/10.25f,
214+
SizingMode::StretchFit,
215+
/*availableHeight=*/20.0f,
216+
SizingMode::StretchFit,
217+
/*lastAvailableWidth=*/10.5f,
218+
SizingMode::StretchFit,
219+
/*lastAvailableHeight=*/20.0f,
220+
/*lastComputedWidth=*/10.5f,
221+
/*lastComputedHeight=*/20.0f,
222+
0.0f,
223+
0.0f,
224+
config_);
225+
226+
EXPECT_TRUE(canUse);
227+
}
228+
229+
} // namespace facebook::yoga

0 commit comments

Comments
 (0)