mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
6f97733bb8
Summary: ComponentName is used by many core component of React Native, such as ComponentDescriptor, ShadowNode, ShadowView and so on. In all those cases this value represents the actual name of the component which came from `concreteComponentName` template parameter of ConcreteShadowNode. In all of those cases, it's raw `char const *` type. So, we don't need to use owning representation of the string (std::string) in all those places. The only exception from this is a part where we receive the name of the component from JS side. In this case, the source string comes from JS and has to be analyzed as a character sequence to find corresponding ComponentDescriptor. In my experiments, 20% of the time during diffing is spent on copying (this) `std::string`. Reviewed By: mdvacca Differential Revision: D15844407 fbshipit-source-id: a2e71505e22d09107e001bdf661d4a826bcf2dea
327 lines
12 KiB
C++
327 lines
12 KiB
C++
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#include <memory>
|
|
|
|
#include <gtest/gtest.h>
|
|
#include <react/core/ConcreteShadowNode.h>
|
|
#include <react/core/ShadowNode.h>
|
|
|
|
#include "TestComponent.h"
|
|
|
|
using namespace facebook::react;
|
|
|
|
TEST(ShadowNodeTest, handleProps) {
|
|
const auto &raw = RawProps(folly::dynamic::object("nativeID", "abc"));
|
|
auto parser = RawPropsParser();
|
|
parser.prepare<Props>();
|
|
raw.parse(parser);
|
|
|
|
auto props = std::make_shared<Props>(Props(), raw);
|
|
|
|
// Props are not sealed after applying raw props.
|
|
ASSERT_FALSE(props->getSealed());
|
|
|
|
ASSERT_STREQ(props->nativeId.c_str(), "abc");
|
|
}
|
|
|
|
TEST(ShadowNodeTest, handleShadowNodeCreation) {
|
|
auto componentDescriptor = TestComponentDescriptor(nullptr);
|
|
auto node = std::make_shared<TestShadowNode>(
|
|
ShadowNodeFragment{
|
|
/* .tag = */ 9,
|
|
/* .surfaceId = */ 1,
|
|
/* .props = */ std::make_shared<const TestProps>(),
|
|
/* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(),
|
|
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
|
|
},
|
|
componentDescriptor);
|
|
|
|
ASSERT_FALSE(node->getSealed());
|
|
ASSERT_STREQ(node->getComponentName(), "Test");
|
|
ASSERT_EQ(node->getTag(), 9);
|
|
ASSERT_EQ(node->getSurfaceId(), 1);
|
|
ASSERT_EQ(node->getEventEmitter(), nullptr);
|
|
ASSERT_EQ(node->getChildren().size(), 0);
|
|
|
|
node->sealRecursive();
|
|
ASSERT_TRUE(node->getSealed());
|
|
ASSERT_TRUE(node->getProps()->getSealed());
|
|
}
|
|
|
|
TEST(ShadowNodeTest, handleShadowNodeSimpleCloning) {
|
|
auto componentDescriptor = TestComponentDescriptor(nullptr);
|
|
auto node = std::make_shared<TestShadowNode>(
|
|
ShadowNodeFragment{
|
|
/* .tag = */ 9,
|
|
/* .surfaceId = */ 1,
|
|
/* .props = */ std::make_shared<const TestProps>(),
|
|
/* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(),
|
|
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
|
|
},
|
|
componentDescriptor);
|
|
auto node2 = std::make_shared<TestShadowNode>(*node, ShadowNodeFragment{});
|
|
|
|
ASSERT_STREQ(node->getComponentName(), "Test");
|
|
ASSERT_EQ(node->getTag(), 9);
|
|
ASSERT_EQ(node->getSurfaceId(), 1);
|
|
ASSERT_EQ(node->getEventEmitter(), nullptr);
|
|
}
|
|
|
|
TEST(ShadowNodeTest, handleShadowNodeMutation) {
|
|
auto componentDescriptor = TestComponentDescriptor(nullptr);
|
|
auto props = std::make_shared<const TestProps>();
|
|
auto node1 = std::make_shared<TestShadowNode>(
|
|
ShadowNodeFragment{
|
|
/* .tag = */ 1,
|
|
/* .surfaceId = */ 1,
|
|
/* .props = */ std::make_shared<const TestProps>(),
|
|
/* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(),
|
|
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
|
|
},
|
|
componentDescriptor);
|
|
auto node2 = std::make_shared<TestShadowNode>(
|
|
ShadowNodeFragment{
|
|
/* .tag = */ 2,
|
|
/* .surfaceId = */ 1,
|
|
/* .props = */ std::make_shared<const TestProps>(),
|
|
/* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(),
|
|
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
|
|
},
|
|
componentDescriptor);
|
|
auto node3 = std::make_shared<TestShadowNode>(
|
|
ShadowNodeFragment{
|
|
/* .tag = */ 3,
|
|
/* .surfaceId = */ 1,
|
|
/* .props = */ std::make_shared<const TestProps>(),
|
|
/* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(),
|
|
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
|
|
},
|
|
componentDescriptor);
|
|
|
|
node1->appendChild(node2);
|
|
node1->appendChild(node3);
|
|
auto node1Children = node1->getChildren();
|
|
ASSERT_EQ(node1Children.size(), 2);
|
|
ASSERT_EQ(node1Children.at(0), node2);
|
|
ASSERT_EQ(node1Children.at(1), node3);
|
|
|
|
auto node4 = std::make_shared<TestShadowNode>(*node2, ShadowNodeFragment{});
|
|
node1->replaceChild(node2, node4);
|
|
node1Children = node1->getChildren();
|
|
ASSERT_EQ(node1Children.size(), 2);
|
|
ASSERT_EQ(node1Children.at(0), node4);
|
|
ASSERT_EQ(node1Children.at(1), node3);
|
|
|
|
// Seal the entire tree.
|
|
node1->sealRecursive();
|
|
ASSERT_TRUE(node1->getSealed());
|
|
ASSERT_TRUE(node3->getSealed());
|
|
ASSERT_TRUE(node4->getSealed());
|
|
|
|
// No more mutation after sealing.
|
|
EXPECT_THROW(node4->setLocalData(nullptr), std::runtime_error);
|
|
|
|
auto node5 = std::make_shared<TestShadowNode>(*node4, ShadowNodeFragment{});
|
|
node5->setLocalData(nullptr);
|
|
ASSERT_EQ(node5->getLocalData(), nullptr);
|
|
}
|
|
|
|
TEST(ShadowNodeTest, handleCloneFunction) {
|
|
auto componentDescriptor = TestComponentDescriptor(nullptr);
|
|
|
|
auto firstNode = std::make_shared<TestShadowNode>(
|
|
ShadowNodeFragment{
|
|
/* .tag = */ 9,
|
|
/* .surfaceId = */ 1,
|
|
/* .props = */ std::make_shared<const TestProps>(),
|
|
/* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(),
|
|
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
|
|
},
|
|
componentDescriptor);
|
|
|
|
auto firstNodeClone = firstNode->clone({});
|
|
|
|
// Those two nodes are *not* same.
|
|
ASSERT_NE(firstNode, firstNodeClone);
|
|
|
|
// `secondNodeClone` is an instance of `TestShadowNode`.
|
|
ASSERT_NE(
|
|
std::dynamic_pointer_cast<const TestShadowNode>(firstNodeClone), nullptr);
|
|
|
|
// Both nodes have same content.
|
|
ASSERT_EQ(firstNode->getTag(), firstNodeClone->getTag());
|
|
ASSERT_EQ(firstNode->getSurfaceId(), firstNodeClone->getSurfaceId());
|
|
ASSERT_EQ(firstNode->getProps(), firstNodeClone->getProps());
|
|
}
|
|
|
|
TEST(ShadowNodeTest, handleLocalData) {
|
|
auto componentDescriptor = TestComponentDescriptor(nullptr);
|
|
auto localData42 = std::make_shared<TestLocalData>();
|
|
localData42->setNumber(42);
|
|
|
|
auto anotherLocalData42 = std::make_shared<TestLocalData>();
|
|
anotherLocalData42->setNumber(42);
|
|
|
|
auto localDataOver9000 = std::make_shared<TestLocalData>();
|
|
localDataOver9000->setNumber(9001);
|
|
auto props = std::make_shared<const TestProps>();
|
|
auto firstNode = std::make_shared<TestShadowNode>(
|
|
ShadowNodeFragment{
|
|
/* .tag = */ 9,
|
|
/* .surfaceId = */ 1,
|
|
/* .props = */ props,
|
|
/* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(),
|
|
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
|
|
},
|
|
componentDescriptor);
|
|
auto secondNode = std::make_shared<TestShadowNode>(
|
|
ShadowNodeFragment{
|
|
/* .tag = */ 9,
|
|
/* .surfaceId = */ 1,
|
|
/* .props = */ props,
|
|
/* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(),
|
|
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
|
|
},
|
|
componentDescriptor);
|
|
auto thirdNode = std::make_shared<TestShadowNode>(
|
|
ShadowNodeFragment{
|
|
/* .tag = */ 9,
|
|
/* .surfaceId = */ 1,
|
|
/* .props = */ props,
|
|
/* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(),
|
|
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
|
|
},
|
|
componentDescriptor);
|
|
|
|
firstNode->setLocalData(localData42);
|
|
secondNode->setLocalData(localData42);
|
|
thirdNode->setLocalData(localDataOver9000);
|
|
|
|
// LocalData object are compared by pointer, not by value.
|
|
ASSERT_EQ(firstNode->getLocalData(), secondNode->getLocalData());
|
|
ASSERT_NE(firstNode->getLocalData(), thirdNode->getLocalData());
|
|
secondNode->setLocalData(anotherLocalData42);
|
|
ASSERT_NE(firstNode->getLocalData(), secondNode->getLocalData());
|
|
|
|
// LocalData cannot be changed for sealed shadow node.
|
|
secondNode->sealRecursive();
|
|
ASSERT_ANY_THROW(secondNode->setLocalData(localDataOver9000));
|
|
}
|
|
|
|
TEST(ShadowNodeTest, handleBacktracking) {
|
|
/*
|
|
* The structure:
|
|
* <A>
|
|
* <AA/>
|
|
* <AB>
|
|
* <ABA/>
|
|
* <ABB/>
|
|
* <ABC/>
|
|
* </AB>
|
|
* <AC/>
|
|
* </A>
|
|
*/
|
|
|
|
auto componentDescriptor = TestComponentDescriptor(nullptr);
|
|
auto props = std::make_shared<const TestProps>();
|
|
|
|
auto nodeAA = std::make_shared<TestShadowNode>(
|
|
ShadowNodeFragment{
|
|
/* .tag = */ ShadowNodeFragment::tagPlaceholder(),
|
|
/* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(),
|
|
/* .props = */ props,
|
|
/* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(),
|
|
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
|
|
},
|
|
componentDescriptor);
|
|
|
|
auto nodeABA = std::make_shared<TestShadowNode>(
|
|
ShadowNodeFragment{
|
|
/* .tag = */ ShadowNodeFragment::tagPlaceholder(),
|
|
/* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(),
|
|
/* .props = */ props,
|
|
/* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(),
|
|
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
|
|
},
|
|
componentDescriptor);
|
|
auto nodeABB = std::make_shared<TestShadowNode>(
|
|
ShadowNodeFragment{
|
|
/* .tag = */ ShadowNodeFragment::tagPlaceholder(),
|
|
/* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(),
|
|
/* .props = */ props,
|
|
/* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(),
|
|
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
|
|
},
|
|
componentDescriptor);
|
|
auto nodeABC = std::make_shared<TestShadowNode>(
|
|
ShadowNodeFragment{
|
|
/* .tag = */ ShadowNodeFragment::tagPlaceholder(),
|
|
/* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(),
|
|
/* .props = */ props,
|
|
/* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(),
|
|
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
|
|
},
|
|
componentDescriptor);
|
|
|
|
auto nodeABChildren = std::make_shared<SharedShadowNodeList>(
|
|
SharedShadowNodeList{nodeABA, nodeABB, nodeABC});
|
|
auto nodeAB = std::make_shared<TestShadowNode>(
|
|
ShadowNodeFragment{
|
|
/* .tag = */ ShadowNodeFragment::tagPlaceholder(),
|
|
/* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(),
|
|
/* .props = */ props,
|
|
/* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(),
|
|
/* .children = */ nodeABChildren,
|
|
},
|
|
componentDescriptor);
|
|
|
|
auto nodeAC = std::make_shared<TestShadowNode>(
|
|
ShadowNodeFragment{
|
|
/* .tag = */ ShadowNodeFragment::tagPlaceholder(),
|
|
/* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(),
|
|
/* .props = */ props,
|
|
/* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(),
|
|
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
|
|
},
|
|
componentDescriptor);
|
|
|
|
auto nodeAChildren = std::make_shared<SharedShadowNodeList>(
|
|
SharedShadowNodeList{nodeAA, nodeAB, nodeAC});
|
|
auto nodeA = std::make_shared<TestShadowNode>(
|
|
ShadowNodeFragment{
|
|
/* .tag = */ ShadowNodeFragment::tagPlaceholder(),
|
|
/* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(),
|
|
/* .props = */ props,
|
|
/* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(),
|
|
/* .children = */ nodeAChildren,
|
|
},
|
|
componentDescriptor);
|
|
|
|
auto nodeZ = std::make_shared<TestShadowNode>(
|
|
ShadowNodeFragment{
|
|
/* .tag = */ ShadowNodeFragment::tagPlaceholder(),
|
|
/* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(),
|
|
/* .props = */ props,
|
|
/* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(),
|
|
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
|
|
},
|
|
componentDescriptor);
|
|
|
|
std::vector<std::reference_wrapper<const ShadowNode>> ancestors = {};
|
|
|
|
// Negative case:
|
|
auto ancestors1 = nodeZ->getAncestors(*nodeA);
|
|
ASSERT_EQ(ancestors1.size(), 0);
|
|
|
|
// Positive case:
|
|
auto ancestors2 = nodeABC->getAncestors(*nodeA);
|
|
ASSERT_EQ(ancestors2.size(), 2);
|
|
ASSERT_EQ(&ancestors2[0].first.get(), nodeA.get());
|
|
ASSERT_EQ(&ancestors2[1].first.get(), nodeAB.get());
|
|
}
|