first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,268 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "AnimationTestsBase.h"
#include <react/renderer/animated/nodes/ColorAnimatedNode.h>
#include <react/renderer/animated/nodes/ObjectAnimatedNode.h>
#include <react/renderer/core/ReactRootViewTagGenerator.h>
#include <react/renderer/graphics/Color.h>
namespace facebook::react {
class AnimatedNodeTests : public AnimationTestsBase {};
TEST_F(AnimatedNodeTests, setAnimatedNodeValue) {
initNodesManager();
auto rootTag = getNextRootViewTag();
auto animatedNodeTag = ++rootTag;
nodesManager_->createAnimatedNode(
animatedNodeTag,
folly::dynamic::object("type", "value")("value", 0)("offset", 5));
EXPECT_EQ(nodeNeedsUpdate(animatedNodeTag), true);
runAnimationFrame(0);
EXPECT_EQ(nodeNeedsUpdate(animatedNodeTag), false);
nodesManager_->setAnimatedNodeValue(animatedNodeTag, 100);
// ValueAnimatedNode will immediately update value, before Animated updates
// dirtied nodes at next frame
EXPECT_EQ(nodesManager_->getValue(animatedNodeTag), 105);
EXPECT_EQ(nodeNeedsUpdate(animatedNodeTag), true);
runAnimationFrame(0);
EXPECT_EQ(nodeNeedsUpdate(animatedNodeTag), false);
nodesManager_->dropAnimatedNode(animatedNodeTag);
}
TEST_F(AnimatedNodeTests, updatePropsNode) {
initNodesManager();
// Step 1: Build the Nodes graph
auto rootTag = getNextRootViewTag();
// Create ColorNode
auto rTag = ++rootTag;
auto gTag = ++rootTag;
auto bTag = ++rootTag;
auto aTag = ++rootTag;
auto colorNodeTag = ++rootTag;
nodesManager_->createAnimatedNode(
rTag, folly::dynamic::object("type", "value")("value", 0)("offset", 0));
nodesManager_->createAnimatedNode(
gTag, folly::dynamic::object("type", "value")("value", 255)("offset", 0));
nodesManager_->createAnimatedNode(
bTag, folly::dynamic::object("type", "value")("value", 0)("offset", 0));
nodesManager_->createAnimatedNode(
aTag, folly::dynamic::object("type", "value")("value", 0.5)("offset", 0));
nodesManager_->createAnimatedNode(
colorNodeTag,
folly::dynamic::object("type", "color")("r", rTag)("g", gTag)("b", bTag)(
"a", aTag));
nodesManager_->connectAnimatedNodes(rTag, colorNodeTag);
nodesManager_->connectAnimatedNodes(gTag, colorNodeTag);
nodesManager_->connectAnimatedNodes(bTag, colorNodeTag);
nodesManager_->connectAnimatedNodes(aTag, colorNodeTag);
// Create opacity ValueNode
auto opacityNodeTag = ++rootTag;
nodesManager_->createAnimatedNode(
opacityNodeTag,
folly::dynamic::object("type", "value")("value", 0.8f)("offset", 0));
// Create StyleNode
auto styleNodeTag = ++rootTag;
nodesManager_->createAnimatedNode(
styleNodeTag,
folly::dynamic::object("type", "style")(
"style",
folly::dynamic::object("backgroundColor", colorNodeTag)(
"opacity", opacityNodeTag)));
nodesManager_->connectAnimatedNodes(colorNodeTag, styleNodeTag);
nodesManager_->connectAnimatedNodes(opacityNodeTag, styleNodeTag);
// Create PropsNode
auto propsNodeTag = ++rootTag;
nodesManager_->createAnimatedNode(
propsNodeTag,
folly::dynamic::object("type", "props")(
"props", folly::dynamic::object("style", styleNodeTag)));
nodesManager_->connectAnimatedNodes(styleNodeTag, propsNodeTag);
// Connect PropsNode to View
auto viewTag = ++rootTag;
nodesManager_->connectAnimatedNodeToView(propsNodeTag, viewTag);
runAnimationFrame(0);
// Step 2: Update backgroundColor
{
nodesManager_->setAnimatedNodeValue(bTag, 100);
nodesManager_->setAnimatedNodeValue(aTag, 0.3);
// Confirm the nodes graph is correctly marked dirty
EXPECT_EQ(nodeNeedsUpdate(rTag), false);
EXPECT_EQ(nodeNeedsUpdate(gTag), false);
EXPECT_EQ(nodeNeedsUpdate(bTag), true);
EXPECT_EQ(nodeNeedsUpdate(aTag), true);
EXPECT_EQ(nodeNeedsUpdate(opacityNodeTag), false);
// connected style/prop nodes are not marked dirty but they will be updated
// at next render
EXPECT_EQ(nodeNeedsUpdate(styleNodeTag), false);
EXPECT_EQ(nodeNeedsUpdate(propsNodeTag), false);
// Flush changes
runAnimationFrame(0);
// Check props commit done via MountingManager
auto color =
static_cast<Color>(lastCommittedProps["backgroundColor"].asInt());
EXPECT_EQ(redFromColor({color}), 0);
EXPECT_EQ(greenFromColor({color}), 255);
EXPECT_EQ(blueFromColor({color}), 100);
EXPECT_EQ(alphaFromColor({color}), static_cast<uint8_t>(0.3 * 255));
EXPECT_EQ(lastUpdatedNodeTag, viewTag);
}
// Step 3: Update opacity
{
nodesManager_->setAnimatedNodeValue(opacityNodeTag, 0.1f);
// Confirm the nodes graph is correctly marked dirty
EXPECT_EQ(nodeNeedsUpdate(rTag), false);
EXPECT_EQ(nodeNeedsUpdate(gTag), false);
EXPECT_EQ(nodeNeedsUpdate(bTag), false);
EXPECT_EQ(nodeNeedsUpdate(aTag), false);
EXPECT_EQ(nodeNeedsUpdate(opacityNodeTag), true);
// connected style/prop nodes are not marked dirty but they will be updated
// at next render
EXPECT_EQ(nodeNeedsUpdate(styleNodeTag), false);
EXPECT_EQ(nodeNeedsUpdate(propsNodeTag), false);
// Flush changes
runAnimationFrame(0);
// Check props commit done via MountingManager
EXPECT_EQ(lastCommittedProps["opacity"], 0.1f);
EXPECT_EQ(lastUpdatedNodeTag, viewTag);
}
}
TEST_F(AnimatedNodeTests, ModulusAnimatedNode) {
initNodesManager();
auto rootTag = getNextRootViewTag();
auto valueTag = ++rootTag;
auto moduloTag = ++rootTag;
nodesManager_->createAnimatedNode(
valueTag,
folly::dynamic::object("type", "value")("value", 0)("offset", 1));
nodesManager_->createAnimatedNode(
moduloTag,
folly::dynamic::object("type", "modulus")("input", valueTag)(
"modulus", 3.1));
nodesManager_->connectAnimatedNodes(valueTag, moduloTag);
runAnimationFrame(0);
nodesManager_->setAnimatedNodeValue(valueTag, 4.1);
runAnimationFrame(0);
EXPECT_EQ(nodesManager_->getValue(valueTag), 5.1);
EXPECT_EQ(nodesManager_->getValue(moduloTag), std::fmod(5.1, 3.1));
nodesManager_->setAnimatedNodeValue(valueTag, 7.6);
runAnimationFrame(0);
EXPECT_EQ(nodesManager_->getValue(valueTag), 8.6);
EXPECT_EQ(nodesManager_->getValue(moduloTag), std::fmod(8.6, 3.1));
}
TEST_F(AnimatedNodeTests, DiffClampAnimatedNode) {
initNodesManager();
auto rootTag = getNextRootViewTag();
auto valueTag = ++rootTag;
auto diffClampTag = ++rootTag;
nodesManager_->createAnimatedNode(
valueTag,
folly::dynamic::object("type", "value")("value", 4)("offset", 0));
nodesManager_->createAnimatedNode(
diffClampTag,
folly::dynamic::object("type", "diffclamp")("input", valueTag)("min", 1)(
"max", 2));
nodesManager_->connectAnimatedNodes(valueTag, diffClampTag);
runAnimationFrame(0);
EXPECT_EQ(nodesManager_->getValue(diffClampTag), 2);
nodesManager_->setAnimatedNodeValue(valueTag, 2);
runAnimationFrame(0);
EXPECT_EQ(nodesManager_->getValue(diffClampTag), 1);
}
TEST_F(AnimatedNodeTests, ObjectAnimatedNode) {
initNodesManager();
auto rootTag = getNextRootViewTag();
auto valueTag = ++rootTag;
auto objectTag = ++rootTag;
nodesManager_->createAnimatedNode(
valueTag,
folly::dynamic::object("type", "value")("value", 4)("offset", 0));
nodesManager_->createAnimatedNode(
objectTag,
folly::dynamic::object("type", "object")(
"value",
folly::dynamic::array(
folly::dynamic::object(
"translate3d",
folly::dynamic::object("x", 1)("y", 0)("z", 0)),
folly::dynamic::object(
"rotate3d",
folly::dynamic::object("x", 1)("y", 0)("z", 0)(
"angle", "180deg")),
folly::dynamic::object(
"scale3d", folly::dynamic::object("nodeTag", valueTag)))));
nodesManager_->connectAnimatedNodes(valueTag, objectTag);
const auto objectNode =
nodesManager_->getAnimatedNode<ObjectAnimatedNode>(objectTag);
folly::dynamic collectedProps = folly::dynamic::object();
objectNode->collectViewUpdates("test", collectedProps);
const auto expected = folly::dynamic::object(
"test",
folly::dynamic::array(
folly::dynamic::object(
"translate3d", folly::dynamic::object("x", 1)("y", 0)("z", 0)),
folly::dynamic::object(
"rotate3d",
folly::dynamic::object("x", 1)("y", 0)("z", 0)(
"angle", "180deg")),
folly::dynamic::object("scale3d", 4)));
EXPECT_EQ(collectedProps["test"].size(), 3);
EXPECT_EQ(collectedProps["test"][0]["translate3d"]["x"], 1);
EXPECT_EQ(collectedProps["test"][1]["rotate3d"]["y"], 0);
EXPECT_EQ(collectedProps["test"][1]["rotate3d"]["angle"], "180deg");
EXPECT_EQ(collectedProps["test"][2]["scale3d"], 4);
}
} // namespace facebook::react

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "AnimationTestsBase.h"
#include <react/renderer/animated/drivers/AnimationDriverUtils.h>
#include <react/renderer/core/ReactRootViewTagGenerator.h>
namespace facebook::react {
class AnimationDriverTests : public AnimationTestsBase {
protected:
double round(double value) noexcept {
// Round to 2 decimal places
return std::ceil(value * 100) / 100;
}
};
TEST_F(AnimationDriverTests, framesAnimation) {
initNodesManager();
auto rootTag = getNextRootViewTag();
auto valueNodeTag = ++rootTag;
nodesManager_->createAnimatedNode(
valueNodeTag,
folly::dynamic::object("type", "value")("value", 0)("offset", 0));
const auto animationId = 1;
const auto frames = folly::dynamic::array(0.0f, 0.1f, 0.4f, 0.9f, 1.0f);
const auto toValue = 100;
nodesManager_->startAnimatingNode(
animationId,
valueNodeTag,
folly::dynamic::object("type", "frames")("frames", frames)(
"toValue", toValue),
std::nullopt);
const double startTimeInTick = 12345;
runAnimationFrame(startTimeInTick);
EXPECT_EQ(round(nodesManager_->getValue(valueNodeTag).value()), 0);
runAnimationFrame(startTimeInTick + SingleFrameIntervalMs * 2.5);
EXPECT_EQ(round(nodesManager_->getValue(valueNodeTag).value()), 65);
runAnimationFrame(startTimeInTick + SingleFrameIntervalMs * 3);
EXPECT_EQ(round(nodesManager_->getValue(valueNodeTag).value()), 90);
runAnimationFrame(startTimeInTick + SingleFrameIntervalMs * 4);
EXPECT_EQ(round(nodesManager_->getValue(valueNodeTag).value()), toValue);
runAnimationFrame(startTimeInTick + SingleFrameIntervalMs * 10);
EXPECT_EQ(round(nodesManager_->getValue(valueNodeTag).value()), toValue);
}
} // namespace facebook::react

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#ifdef _WIN32
#include <folly/portability/Unistd.h>
#include <folly/portability/Windows.h>
#endif
#include <gtest/gtest.h>
#include <react/renderer/animated/NativeAnimatedNodesManager.h>
namespace facebook::react {
class AnimationTestsBase : public testing::Test {
public:
AnimationTestsBase() = default;
protected:
void initNodesManager() noexcept
{
nodesManager_.reset();
nodesManager_ = std::make_shared<NativeAnimatedNodesManager>(
[this](Tag reactTag, const folly::dynamic &changedProps) {
lastUpdatedNodeTag = reactTag;
lastCommittedProps = changedProps;
},
[this](const std::unordered_map<Tag, folly::dynamic> &nodesProps) {
if (!nodesProps.empty()) {
lastUpdatedNodeTag = nodesProps.begin()->first;
lastCommittedProps = nodesProps.begin()->second;
}
});
NativeAnimatedNodesManager::isOnRenderThread_ = true;
}
bool nodeNeedsUpdate(Tag nodeTag) const
{
return nodesManager_->updatedNodeTags_.contains(nodeTag);
}
void runAnimationFrame(double timestamp)
{
nodesManager_->onAnimationFrame(timestamp);
}
std::shared_ptr<NativeAnimatedNodesManager> nodesManager_;
folly::dynamic lastCommittedProps{folly::dynamic::object()};
Tag lastUpdatedNodeTag{};
};
} // namespace facebook::react

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "AnimationTestsBase.h"
#include <react/renderer/components/scrollview/ScrollEvent.h>
#include <react/renderer/core/ReactRootViewTagGenerator.h>
namespace facebook::react {
class EventAnimationDriverTests : public AnimationTestsBase {};
TEST_F(EventAnimationDriverTests, subscribeToViewEvent) {
initNodesManager();
auto tag = getNextRootViewTag();
auto viewTag = ++tag;
const auto animatedValueTag = ++tag;
const auto animatedValueTag2 = ++tag;
const auto eventName = "scroll";
const folly::dynamic valueNodeConfig =
folly::dynamic::object("type", "value")("value", 0)("offset", 0);
// Call onRender once to initialize thread local
nodesManager_->onRender();
{
nodesManager_->createAnimatedNode(animatedValueTag, valueNodeConfig);
folly::dynamic eventMapping =
folly::dynamic::object("animatedValueTag", animatedValueTag)(
"nativeEventPath", folly::dynamic::array("contentOffset", "y"));
nodesManager_->addAnimatedEventToView(viewTag, eventName, eventMapping);
}
{
nodesManager_->createAnimatedNode(animatedValueTag2, valueNodeConfig);
folly::dynamic eventMapping =
folly::dynamic::object("animatedValueTag", animatedValueTag2)(
"nativeEventPath", folly::dynamic::array("zoomScale"));
nodesManager_->addAnimatedEventToView(viewTag, eventName, eventMapping);
}
EXPECT_EQ(nodesManager_->getValue(animatedValueTag), 0);
EXPECT_EQ(nodesManager_->getValue(animatedValueTag2), 0);
auto scrollEvent = std::make_shared<ScrollEvent>();
scrollEvent->contentSize = {.width = 1, .height = 2};
scrollEvent->contentOffset = {.x = 3, .y = 4};
scrollEvent->contentInset = {.left = 5, .top = 6, .right = 7, .bottom = 8};
scrollEvent->containerSize = {.width = 9, .height = 10};
scrollEvent->zoomScale = 11.0f;
const std::string eventType{eventName};
const SharedEventPayload payload = scrollEvent;
(*nodesManager_->getEventEmitterListener())(viewTag, eventName, *scrollEvent);
EXPECT_EQ(nodesManager_->getValue(animatedValueTag), 4);
EXPECT_EQ(nodesManager_->getValue(animatedValueTag2), 11);
}
} // namespace facebook::react