Skip to content

Commit de1b4df

Browse files
DevmateUtgenCppGeneralFBOrg Botfacebook-github-bot
authored andcommitted
xplat/js/react-native-github/packages/react-native/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp
Reviewed By: cortinico Differential Revision: D118418728
1 parent ce621bb commit de1b4df

1 file changed

Lines changed: 296 additions & 0 deletions

File tree

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
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 <react/renderer/animationbackend/AnimationBackend.h>
9+
10+
#include <gtest/gtest.h>
11+
#include <react/renderer/core/ComponentDescriptor.h>
12+
#include <react/renderer/uimanager/UIManager.h>
13+
#include <react/renderer/uimanager/UIManagerDelegate.h>
14+
#include <react/utils/ContextContainer.h>
15+
16+
#include <memory>
17+
#include <string>
18+
#include <utility>
19+
#include <vector>
20+
21+
namespace facebook::react {
22+
23+
namespace {
24+
25+
class TestAnimationChoreographer final : public AnimationChoreographer {
26+
public:
27+
void resume() override {
28+
++resumeCount;
29+
}
30+
31+
void pause() override {
32+
++pauseCount;
33+
}
34+
35+
AnimationTimestamp now() const override {
36+
return currentTimestamp;
37+
}
38+
39+
int resumeCount{0};
40+
int pauseCount{0};
41+
AnimationTimestamp currentTimestamp{123.0};
42+
};
43+
44+
class RecordingUIManagerDelegate final : public UIManagerDelegate {
45+
public:
46+
void uiManagerDidFinishTransaction(
47+
std::shared_ptr<const MountingCoordinator> /*mountingCoordinator*/,
48+
bool /*mountSynchronously*/) override {}
49+
50+
void uiManagerDidCreateShadowNode(const ShadowNode& /*shadowNode*/) override {
51+
}
52+
53+
void uiManagerDidDispatchCommand(
54+
const std::shared_ptr<const ShadowNode>& /*shadowNode*/,
55+
const std::string& /*commandName*/,
56+
const folly::dynamic& /*args*/) override {}
57+
58+
void uiManagerDidSendAccessibilityEvent(
59+
const std::shared_ptr<const ShadowNode>& /*shadowNode*/,
60+
const std::string& /*eventType*/) override {}
61+
62+
void uiManagerDidSetIsJSResponder(
63+
const std::shared_ptr<const ShadowNode>& /*shadowNode*/,
64+
bool /*isJSResponder*/,
65+
bool /*blockNativeResponder*/) override {}
66+
67+
void uiManagerShouldSynchronouslyUpdateViewOnUIThread(
68+
Tag tag,
69+
const folly::dynamic& props) override {
70+
synchronousUpdates.push_back({tag, props});
71+
}
72+
73+
void uiManagerDidUpdateShadowTree(
74+
const std::unordered_map<Tag, folly::dynamic>& /*tagToProps*/) override {}
75+
76+
void uiManagerShouldAddEventListener(
77+
std::shared_ptr<const EventListener> /*listener*/) override {}
78+
79+
void uiManagerShouldRemoveEventListener(
80+
const std::shared_ptr<const EventListener>& /*listener*/) override {}
81+
82+
void uiManagerDidStartSurface(const ShadowTree& /*shadowTree*/) override {}
83+
84+
void uiManagerDidFinishReactCommit(
85+
const ShadowTree& /*shadowTree*/) override {}
86+
87+
void uiManagerDidPromoteReactRevision(
88+
const ShadowTree& /*shadowTree*/) override {}
89+
90+
void uiManagerShouldAddOnSurfaceStartCallback(
91+
OnSurfaceStartCallback&& callback) override {
92+
surfaceStartCallbacks.push_back(std::move(callback));
93+
}
94+
95+
void uiManagerDidCaptureViewSnapshot(Tag /*tag*/, SurfaceId /*surfaceId*/)
96+
override {}
97+
98+
void uiManagerDidSetViewSnapshot(
99+
Tag /*sourceTag*/,
100+
Tag /*targetTag*/,
101+
SurfaceId /*surfaceId*/) override {}
102+
103+
void uiManagerDidClearPendingSnapshots() override {}
104+
105+
std::vector<std::pair<Tag, folly::dynamic>> synchronousUpdates;
106+
std::vector<OnSurfaceStartCallback> surfaceStartCallbacks;
107+
};
108+
109+
class TestComponentDescriptor final : public ComponentDescriptor {
110+
public:
111+
explicit TestComponentDescriptor(
112+
const ComponentDescriptorParameters& parameters)
113+
: ComponentDescriptor(parameters) {}
114+
115+
ComponentHandle getComponentHandle() const override {
116+
return ComponentHandle{1};
117+
}
118+
119+
ComponentName getComponentName() const override {
120+
return "Test";
121+
}
122+
123+
ShadowNodeTraits getTraits() const override {
124+
return ShadowNodeTraits{};
125+
}
126+
127+
std::shared_ptr<ShadowNode> createShadowNode(
128+
const ShadowNodeFragment& /*fragment*/,
129+
const ShadowNodeFamily::Shared& /*family*/) const override {
130+
return nullptr;
131+
}
132+
133+
std::shared_ptr<ShadowNode> cloneShadowNode(
134+
const ShadowNode& /*sourceShadowNode*/,
135+
const ShadowNodeFragment& /*fragment*/) const override {
136+
return nullptr;
137+
}
138+
139+
void appendChild(
140+
const std::shared_ptr<const ShadowNode>& /*parentShadowNode*/,
141+
const std::shared_ptr<const ShadowNode>& /*childShadowNode*/)
142+
const override {}
143+
144+
Props::Shared cloneProps(
145+
const PropsParserContext& /*context*/,
146+
const Props::Shared& /*props*/,
147+
RawProps /*rawProps*/) const override {
148+
return nullptr;
149+
}
150+
151+
State::Shared createInitialState(
152+
const Props::Shared& /*props*/,
153+
const ShadowNodeFamily::Shared& /*family*/) const override {
154+
return nullptr;
155+
}
156+
157+
State::Shared createState(
158+
const ShadowNodeFamily& /*family*/,
159+
const StateData::Shared& /*data*/) const override {
160+
return nullptr;
161+
}
162+
163+
ShadowNodeFamily::Shared createFamily(
164+
const ShadowNodeFamilyFragment& fragment) const override {
165+
return std::make_shared<ShadowNodeFamily>(
166+
fragment, nullptr, EventDispatcher::Weak{}, *this);
167+
}
168+
169+
private:
170+
void adopt(ShadowNode& /*shadowNode*/) const override {}
171+
};
172+
173+
class AnimationBackendTest : public ::testing::Test {
174+
protected:
175+
AnimationBackendTest()
176+
: contextContainer_(std::make_shared<ContextContainer>()),
177+
componentDescriptor_(
178+
ComponentDescriptorParameters{
179+
.eventDispatcher = {},
180+
.contextContainer = contextContainer_,
181+
.flavor = nullptr}) {}
182+
183+
void SetUp() override {
184+
RuntimeExecutor runtimeExecutor =
185+
[](std::function<void(facebook::jsi::Runtime & runtime)>&&
186+
/*callback*/) {};
187+
uiManager_ =
188+
std::make_shared<UIManager>(runtimeExecutor, contextContainer_);
189+
uiManager_->setDelegate(&uiManagerDelegate_);
190+
choreographer_ = std::make_shared<TestAnimationChoreographer>();
191+
animationBackend_ =
192+
std::make_unique<AnimationBackend>(choreographer_, uiManager_);
193+
}
194+
195+
void TearDown() override {
196+
uiManager_->setDelegate(nullptr);
197+
}
198+
199+
std::shared_ptr<const ShadowNodeFamily> createFamily(
200+
Tag tag,
201+
SurfaceId surfaceId) {
202+
return componentDescriptor_.createFamily(
203+
{.tag = tag, .surfaceId = surfaceId, .instanceHandle = nullptr});
204+
}
205+
206+
static AnimatedProps makeAnimatedOpacity(Float opacity) {
207+
AnimatedProps props;
208+
props.props.push_back(
209+
std::make_unique<AnimatedProp<Float>>(PropName::OPACITY, opacity));
210+
return props;
211+
}
212+
213+
std::shared_ptr<ContextContainer> contextContainer_;
214+
TestComponentDescriptor componentDescriptor_;
215+
RecordingUIManagerDelegate uiManagerDelegate_;
216+
std::shared_ptr<TestAnimationChoreographer> choreographer_;
217+
std::shared_ptr<UIManager> uiManager_;
218+
std::unique_ptr<AnimationBackend> animationBackend_;
219+
};
220+
221+
TEST_F(AnimationBackendTest, testStartStopManageChoreographerState) {
222+
auto firstCallbackId = animationBackend_->start(
223+
[](AnimationTimestamp /*timestamp*/) { return AnimationMutations{}; });
224+
auto secondCallbackId = animationBackend_->start(
225+
[](AnimationTimestamp /*timestamp*/) { return AnimationMutations{}; });
226+
227+
EXPECT_NE(firstCallbackId, secondCallbackId);
228+
EXPECT_EQ(choreographer_->resumeCount, 1);
229+
EXPECT_EQ(choreographer_->pauseCount, 0);
230+
231+
animationBackend_->stop(firstCallbackId);
232+
233+
EXPECT_EQ(choreographer_->resumeCount, 1);
234+
EXPECT_EQ(choreographer_->pauseCount, 0);
235+
236+
animationBackend_->stop(secondCallbackId);
237+
238+
EXPECT_EQ(choreographer_->resumeCount, 1);
239+
EXPECT_EQ(choreographer_->pauseCount, 1);
240+
}
241+
242+
TEST_F(AnimationBackendTest, testOnAnimationFrameUsesCallbackSnapshot) {
243+
std::vector<std::string> callbackOrder;
244+
CallbackId secondCallbackId{};
245+
246+
animationBackend_->start([&](AnimationTimestamp /*timestamp*/) {
247+
callbackOrder.push_back("first");
248+
animationBackend_->stop(secondCallbackId);
249+
return AnimationMutations{};
250+
});
251+
secondCallbackId =
252+
animationBackend_->start([&](AnimationTimestamp /*timestamp*/) {
253+
callbackOrder.push_back("second");
254+
return AnimationMutations{};
255+
});
256+
257+
animationBackend_->onAnimationFrame(AnimationTimestamp{1.0});
258+
259+
EXPECT_EQ(callbackOrder, (std::vector<std::string>{"first", "second"}));
260+
261+
callbackOrder.clear();
262+
animationBackend_->onAnimationFrame(AnimationTimestamp{2.0});
263+
264+
EXPECT_EQ(callbackOrder, (std::vector<std::string>{"first"}));
265+
}
266+
267+
TEST_F(AnimationBackendTest, testPushAnimationMutationsForwardsPackedProps) {
268+
const auto surfaceId = SurfaceId{11};
269+
const auto tag = Tag{42};
270+
const auto expectedTimestamp = AnimationTimestamp{321.0};
271+
choreographer_->currentTimestamp = expectedTimestamp;
272+
auto family = createFamily(tag, surfaceId);
273+
AnimationTimestamp observedTimestamp{};
274+
275+
animationBackend_->pushAnimationMutations([&](AnimationTimestamp timestamp) {
276+
observedTimestamp = timestamp;
277+
AnimationMutations mutations;
278+
mutations.batch.push_back(
279+
AnimationMutation{
280+
.tag = tag,
281+
.family = family,
282+
.props = makeAnimatedOpacity(0.25),
283+
.hasLayoutUpdates = false});
284+
return mutations;
285+
});
286+
287+
ASSERT_EQ(observedTimestamp, expectedTimestamp);
288+
ASSERT_EQ(uiManagerDelegate_.synchronousUpdates.size(), 1);
289+
EXPECT_EQ(uiManagerDelegate_.synchronousUpdates[0].first, tag);
290+
EXPECT_EQ(
291+
uiManagerDelegate_.synchronousUpdates[0].second.at("opacity"), 0.25);
292+
}
293+
294+
} // namespace
295+
296+
} // namespace facebook::react

0 commit comments

Comments
 (0)