Skip to content

Commit fbf09b7

Browse files
Reduce animation registry copying and allocation: tests and scaffolding
Tests, C++ API snapshots and Apple test-source exclusions for the change in the parent commit.
1 parent d1c4d54 commit fbf09b7

12 files changed

Lines changed: 237 additions & 9 deletions

‎packages/react-native/Package.swift‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ let reactFabric = RNTarget(
459459
path: "ReactCommon/react/renderer",
460460
excludedPaths: [
461461
"animated/tests",
462+
"animationbackend/tests",
462463
"animations/tests",
463464
"attributedstring/tests",
464465
"core/tests",

‎packages/react-native/ReactCommon/React-Fabric.podspec‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ Pod::Spec.new do |s|
7373

7474
s.subspec "animationbackend" do |ss|
7575
ss.source_files = podspec_sources("react/renderer/animationbackend/**/*.{m,mm,cpp,h}", "react/renderer/animationbackend/**/*.{h}")
76+
ss.exclude_files = "react/renderer/animationbackend/tests"
7677
ss.header_dir = "react/renderer/animationbackend"
7778
end
7879

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
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+
#include <hermes/hermes.h>
10+
#include <react/renderer/animationbackend/AnimatedPropsBuilder.h>
11+
#include <react/renderer/animationbackend/AnimatedPropsRegistry.h>
12+
#include <react/renderer/components/view/ViewComponentDescriptor.h>
13+
#include <react/renderer/element/ComponentBuilder.h>
14+
#include <react/renderer/element/Element.h>
15+
#include <react/renderer/element/testUtils.h>
16+
#include <react/utils/ContextContainer.h>
17+
18+
namespace facebook::react {
19+
20+
class AnimatedPropsRegistryTest : public ::testing::Test {
21+
protected:
22+
ComponentBuilder builder =
23+
simpleComponentBuilder(std::make_shared<ContextContainer>());
24+
std::shared_ptr<ViewShadowNode> node =
25+
builder.build(Element<ViewShadowNode>().surfaceId(1).tag(10));
26+
AnimatedPropsRegistry registry;
27+
28+
void SetUp() override {
29+
registry.initializeSurface(1);
30+
}
31+
32+
void update(AnimatedProps props) {
33+
std::vector<AnimationMutations> batches(1);
34+
batches[0].batch.push_back(
35+
AnimationMutation{
36+
.tag = 10,
37+
.family = node->getFamilyShared(),
38+
.props = std::move(props)});
39+
registry.update(batches);
40+
}
41+
42+
void updateRaw(folly::dynamic props) {
43+
update({{}, std::make_unique<RawProps>(std::move(props))});
44+
}
45+
46+
void expectRawProps(folly::dynamic expected) {
47+
EXPECT_EQ(*snapshot().rawProps, expected);
48+
}
49+
50+
PropsSnapshot& snapshot() {
51+
return *registry.getMap(1).second.at(10);
52+
}
53+
};
54+
55+
TEST_F(AnimatedPropsRegistryTest, rawUpdatesDoNotAllocateTypedProps) {
56+
for (int frame = 0; frame < 10; frame++) {
57+
updateRaw(folly::dynamic::object("opacity", frame / 10.0));
58+
auto& props = snapshot();
59+
EXPECT_EQ(props.props, nullptr);
60+
EXPECT_TRUE(props.propNames.empty());
61+
EXPECT_EQ((*props.rawProps)["opacity"], frame / 10.0);
62+
}
63+
}
64+
65+
TEST_F(AnimatedPropsRegistryTest, updatePreservesInputForSynchronousMount) {
66+
folly::dynamic rawProps = folly::dynamic::object("opacity", 0.25)(
67+
"transform",
68+
folly::dynamic::array(folly::dynamic::object("translateX", 10)));
69+
std::vector<AnimationMutations> batches(1);
70+
batches[0].batch.push_back(
71+
AnimationMutation{
72+
.tag = 10,
73+
.family = node->getFamilyShared(),
74+
.props = AnimatedProps{{}, std::make_unique<RawProps>(rawProps)}});
75+
registry.update(batches);
76+
registry.update(batches);
77+
EXPECT_EQ(batches[0].batch[0].props.rawProps->toDynamic(), rawProps);
78+
EXPECT_EQ(*snapshot().rawProps, rawProps);
79+
}
80+
81+
TEST_F(AnimatedPropsRegistryTest, mergesPendingUpdatesRecursively) {
82+
updateRaw(
83+
folly::dynamic::object("opacity", 0.25)(
84+
"shadowOffset", folly::dynamic::object("width", 2)("height", 3)));
85+
updateRaw(
86+
folly::dynamic::object(
87+
"shadowOffset", folly::dynamic::object("width", 7)));
88+
expectRawProps(
89+
folly::dynamic::object("opacity", 0.25)(
90+
"shadowOffset", folly::dynamic::object("width", 7)("height", 3)));
91+
}
92+
93+
TEST_F(AnimatedPropsRegistryTest, mergesCommittedUpdatesRecursively) {
94+
updateRaw(
95+
folly::dynamic::object("opacity", 0.25)(
96+
"shadowOffset", folly::dynamic::object("width", 2)("height", 3)));
97+
snapshot();
98+
updateRaw(
99+
folly::dynamic::object(
100+
"shadowOffset", folly::dynamic::object("width", 7)));
101+
expectRawProps(
102+
folly::dynamic::object("opacity", 0.25)(
103+
"shadowOffset", folly::dynamic::object("width", 7)("height", 3)));
104+
}
105+
106+
TEST_F(AnimatedPropsRegistryTest, nullDeletesPendingProps) {
107+
updateRaw(
108+
folly::dynamic::object("opacity", 0.25)(
109+
"shadowOffset", folly::dynamic::object("width", 2)("height", 3)));
110+
updateRaw(
111+
folly::dynamic::object("opacity", nullptr)(
112+
"shadowOffset", folly::dynamic::object("width", nullptr)));
113+
expectRawProps(
114+
folly::dynamic::object(
115+
"shadowOffset", folly::dynamic::object("height", 3)));
116+
}
117+
118+
TEST_F(AnimatedPropsRegistryTest, nullDeletesCommittedProps) {
119+
updateRaw(
120+
folly::dynamic::object("opacity", 0.25)(
121+
"shadowOffset", folly::dynamic::object("width", 2)("height", 3)));
122+
snapshot();
123+
updateRaw(
124+
folly::dynamic::object("opacity", nullptr)(
125+
"shadowOffset", folly::dynamic::object("width", nullptr)));
126+
expectRawProps(
127+
folly::dynamic::object(
128+
"shadowOffset", folly::dynamic::object("height", 3)));
129+
}
130+
131+
TEST_F(AnimatedPropsRegistryTest, arraysReplaceInsteadOfMerging) {
132+
updateRaw(
133+
folly::dynamic::object(
134+
"transform",
135+
folly::dynamic::array(
136+
folly::dynamic::object("translateX", 2),
137+
folly::dynamic::object("scale", 3))));
138+
snapshot();
139+
auto transform =
140+
folly::dynamic::array(folly::dynamic::object("rotate", "1rad"));
141+
updateRaw(folly::dynamic::object("transform", transform));
142+
EXPECT_EQ((*snapshot().rawProps)["transform"], transform);
143+
}
144+
145+
TEST_F(AnimatedPropsRegistryTest, objectPatchReplacesScalarAndRemovesNulls) {
146+
updateRaw(folly::dynamic::object("value", 5));
147+
snapshot();
148+
updateRaw(
149+
folly::dynamic::object(
150+
"value", folly::dynamic::object("width", 2)("height", nullptr)));
151+
expectRawProps(
152+
folly::dynamic::object("value", folly::dynamic::object("width", 2)));
153+
updateRaw(folly::dynamic::object("value", 7));
154+
expectRawProps(folly::dynamic::object("value", 7));
155+
}
156+
157+
TEST_F(AnimatedPropsRegistryTest, typedPropsCanFollowRawProps) {
158+
updateRaw(folly::dynamic::object("opacity", 0.25));
159+
snapshot();
160+
AnimatedPropsBuilder builder;
161+
builder.setOpacity(0.75);
162+
update(builder.get());
163+
auto& props = snapshot();
164+
ASSERT_NE(props.props, nullptr);
165+
EXPECT_FLOAT_EQ(props.props->opacity, 0.75);
166+
EXPECT_TRUE(props.propNames.contains(OPACITY));
167+
EXPECT_EQ((*props.rawProps)["opacity"], 0.25);
168+
BaseViewProps viewProps;
169+
updateProp(OPACITY, viewProps, props);
170+
EXPECT_FLOAT_EQ(viewProps.opacity, 0.75);
171+
}
172+
173+
TEST_F(AnimatedPropsRegistryTest, rawPropsCanFollowTypedProps) {
174+
AnimatedPropsBuilder builder;
175+
builder.setOpacity(0.75);
176+
update(builder.get());
177+
snapshot();
178+
updateRaw(folly::dynamic::object("opacity", 0.25));
179+
auto& props = snapshot();
180+
ASSERT_NE(props.props, nullptr);
181+
EXPECT_FLOAT_EQ(props.props->opacity, 0.75);
182+
EXPECT_TRUE(props.propNames.contains(OPACITY));
183+
EXPECT_EQ((*props.rawProps)["opacity"], 0.25);
184+
}
185+
186+
TEST_F(AnimatedPropsRegistryTest, mergesTypedUpdatesAcrossCommits) {
187+
AnimatedPropsBuilder builder;
188+
builder.setOpacity(0.75);
189+
builder.setShadowRadius(2);
190+
update(builder.get());
191+
snapshot();
192+
builder.setOpacity(0.5);
193+
update(builder.get());
194+
auto& props = snapshot();
195+
ASSERT_NE(props.props, nullptr);
196+
EXPECT_FLOAT_EQ(props.props->opacity, 0.5);
197+
EXPECT_FLOAT_EQ(props.props->shadowRadius, 2);
198+
EXPECT_EQ(props.propNames.size(), 2);
199+
}
200+
201+
TEST_F(AnimatedPropsRegistryTest, jsiRawPropsAreConverted) {
202+
auto runtime = hermes::makeHermesRuntime();
203+
{
204+
auto object = jsi::Object(*runtime);
205+
object.setProperty(*runtime, "opacity", 0.25);
206+
update(
207+
{{},
208+
std::make_unique<RawProps>(*runtime, jsi::Value(*runtime, object))});
209+
object.setProperty(*runtime, "opacity", 0.5);
210+
update(
211+
{{},
212+
std::make_unique<RawProps>(*runtime, jsi::Value(*runtime, object))});
213+
}
214+
runtime.reset();
215+
expectRawProps(folly::dynamic::object("opacity", 0.5));
216+
EXPECT_EQ(snapshot().props, nullptr);
217+
}
218+
219+
TEST_F(AnimatedPropsRegistryTest, stoppedSurfacesIgnoreUpdates) {
220+
registry.clearOnSurfaceStop(1);
221+
updateRaw(folly::dynamic::object("opacity", 0.5));
222+
registry.initializeSurface(1);
223+
EXPECT_TRUE(registry.getMap(1).second.empty());
224+
}
225+
226+
} // namespace facebook::react

‎scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7818,7 +7818,7 @@ struct facebook::react::PropsParserContext {
78187818
}
78197819

78207820
struct facebook::react::PropsSnapshot {
7821-
public facebook::react::BaseViewProps props;
7821+
public std::unique_ptr<facebook::react::BaseViewProps> props;
78227822
public std::unique_ptr<folly::dynamic> rawProps;
78237823
public std::unordered_set<facebook::react::PropName> propNames;
78247824
}

‎scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7578,7 +7578,7 @@ struct facebook::react::PropsParserContext {
75787578
}
75797579

75807580
struct facebook::react::PropsSnapshot {
7581-
public facebook::react::BaseViewProps props;
7581+
public std::unique_ptr<facebook::react::BaseViewProps> props;
75827582
public std::unique_ptr<folly::dynamic> rawProps;
75837583
public std::unordered_set<facebook::react::PropName> propNames;
75847584
}

‎scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7809,7 +7809,7 @@ struct facebook::react::PropsParserContext {
78097809
}
78107810

78117811
struct facebook::react::PropsSnapshot {
7812-
public facebook::react::BaseViewProps props;
7812+
public std::unique_ptr<facebook::react::BaseViewProps> props;
78137813
public std::unique_ptr<folly::dynamic> rawProps;
78147814
public std::unordered_set<facebook::react::PropName> propNames;
78157815
}

‎scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9805,7 +9805,7 @@ struct facebook::react::PropsParserContext {
98059805
}
98069806

98079807
struct facebook::react::PropsSnapshot {
9808-
public facebook::react::BaseViewProps props;
9808+
public std::unique_ptr<facebook::react::BaseViewProps> props;
98099809
public std::unique_ptr<folly::dynamic> rawProps;
98109810
public std::unordered_set<facebook::react::PropName> propNames;
98119811
}

‎scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9621,7 +9621,7 @@ struct facebook::react::PropsParserContext {
96219621
}
96229622

96239623
struct facebook::react::PropsSnapshot {
9624-
public facebook::react::BaseViewProps props;
9624+
public std::unique_ptr<facebook::react::BaseViewProps> props;
96259625
public std::unique_ptr<folly::dynamic> rawProps;
96269626
public std::unordered_set<facebook::react::PropName> propNames;
96279627
}

‎scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9796,7 +9796,7 @@ struct facebook::react::PropsParserContext {
97969796
}
97979797

97989798
struct facebook::react::PropsSnapshot {
9799-
public facebook::react::BaseViewProps props;
9799+
public std::unique_ptr<facebook::react::BaseViewProps> props;
98009800
public std::unique_ptr<folly::dynamic> rawProps;
98019801
public std::unordered_set<facebook::react::PropName> propNames;
98029802
}

‎scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5957,7 +5957,7 @@ struct facebook::react::PropsParserContext {
59575957
}
59585958

59595959
struct facebook::react::PropsSnapshot {
5960-
public facebook::react::BaseViewProps props;
5960+
public std::unique_ptr<facebook::react::BaseViewProps> props;
59615961
public std::unique_ptr<folly::dynamic> rawProps;
59625962
public std::unordered_set<facebook::react::PropName> propNames;
59635963
}

0 commit comments

Comments
 (0)