Files
react-native/ReactCommon/fabric/core/tests/ShadowNodeTest.cpp
T
Valentin Shergin fa773a1446 Fabric: Introducing ShadowNode traitCasts
Summary:
`traitCast` is a special form of static_cast that checks additional requirements (similar to `dynamic_cast`) before performing the cast. We will use that in many places in the coming diffs.

Restructuring the class hierarchy in the previous diff finally allows us to do static casts among ShadowNode and LayoutableShadowNode and other classes (previously it wasn't allowed because of lack of common base class).

Why we don't want to use `dynamic_cast`:
* It's expensive (and we need to do the cast on the hottest fragments of the framework). (See: (1) http://www.stroustrup.com/fast_dynamic_casting.pdf and (2) https://www.youtube.com/watch?v=ARYP83yNAWk Herb Sutter & proposal of `down_cast`).
* It's code-size inefficient, whereas `static_cast` has zero runtime and code-size overhead.
* Removing `dynamic_cast` will allow us finally to opt-out `RTTI` (additional code size and perm wins).

Changelog: [Internal] Fabric-specific internal change.

Reviewed By: sammy-SC

Differential Revision: D20052024

fbshipit-source-id: d293c1cf80deb7817d333d5306d6b32bf3abdb27
2020-02-26 22:08:17 -08:00

293 lines
9.1 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;
class ShadowNodeTest : public ::testing::Test {
protected:
ShadowNodeTest()
: eventDispatcher_(std::shared_ptr<EventDispatcher const>()),
componentDescriptor_(TestComponentDescriptor({eventDispatcher_})) {
/*
* The structure:
* <A>
* <AA/>
* <AB>
* <ABA/>
* <ABB/>
* </AB>
* <AC/>
* </A>
* </Z>
*/
auto props = std::make_shared<const TestProps>();
auto traits = TestShadowNode::BaseTraits();
auto familyAA = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
/* .tag = */ 11,
/* .surfaceId = */ surfaceId_,
/* .eventEmitter = */ nullptr,
},
eventDispatcher_,
componentDescriptor_);
nodeAA_ = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
/* .props = */ props,
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
},
familyAA,
traits);
auto familyABA = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
/* .tag = */ 12,
/* .surfaceId = */ surfaceId_,
/* .eventEmitter = */ nullptr,
},
eventDispatcher_,
componentDescriptor_);
nodeABA_ = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
/* .props = */ props,
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
},
familyABA,
traits);
auto familyABB = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
/* .tag = */ 13,
/* .surfaceId = */ surfaceId_,
/* .eventEmitter = */ nullptr,
},
eventDispatcher_,
componentDescriptor_);
nodeABB_ = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
/* .props = */ props,
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
},
familyABB,
traits);
auto nodeABChildren = std::make_shared<SharedShadowNodeList>(
SharedShadowNodeList{nodeABA_, nodeABB_});
auto familyAB = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
/* .tag = */ 15,
/* .surfaceId = */ surfaceId_,
/* .eventEmitter = */ nullptr,
},
eventDispatcher_,
componentDescriptor_);
nodeAB_ = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
/* .props = */ props,
/* .children = */ nodeABChildren,
},
familyAB,
traits);
auto familyAC = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
/* .tag = */ 16,
/* .surfaceId = */ surfaceId_,
/* .eventEmitter = */ nullptr,
},
eventDispatcher_,
componentDescriptor_);
nodeAC_ = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
/* .props = */ props,
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
},
familyAC,
traits);
auto nodeAChildren = std::make_shared<SharedShadowNodeList>(
SharedShadowNodeList{nodeAA_, nodeAB_, nodeAC_});
auto familyA = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
/* .tag = */ 17,
/* .surfaceId = */ surfaceId_,
/* .eventEmitter = */ nullptr,
},
eventDispatcher_,
componentDescriptor_);
nodeA_ = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
/* .props = */ props,
/* .children = */ nodeAChildren,
},
familyA,
traits);
auto familyZ = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
/* .tag = */ 18,
/* .surfaceId = */ surfaceId_,
/* .eventEmitter = */ nullptr,
},
eventDispatcher_,
componentDescriptor_);
nodeZ_ = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
/* .props = */ props,
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
},
familyZ,
traits);
}
std::shared_ptr<EventDispatcher const> eventDispatcher_;
std::shared_ptr<TestShadowNode> nodeA_;
std::shared_ptr<TestShadowNode> nodeAA_;
std::shared_ptr<TestShadowNode> nodeABA_;
std::shared_ptr<TestShadowNode> nodeABB_;
std::shared_ptr<TestShadowNode> nodeAB_;
std::shared_ptr<TestShadowNode> nodeAC_;
std::shared_ptr<TestShadowNode> nodeZ_;
TestComponentDescriptor componentDescriptor_;
SurfaceId surfaceId_ = 1;
};
TEST_F(ShadowNodeTest, handleShadowNodeCreation) {
EXPECT_FALSE(nodeZ_->getSealed());
EXPECT_STREQ(nodeZ_->getComponentName(), "Test");
EXPECT_EQ(nodeZ_->getTag(), 18);
EXPECT_EQ(nodeZ_->getSurfaceId(), surfaceId_);
EXPECT_EQ(nodeZ_->getEventEmitter(), nullptr);
EXPECT_EQ(nodeZ_->getChildren().size(), 0);
}
TEST_F(ShadowNodeTest, handleSealRecusive) {
nodeZ_->sealRecursive();
EXPECT_TRUE(nodeZ_->getSealed());
EXPECT_TRUE(nodeZ_->getProps()->getSealed());
}
TEST_F(ShadowNodeTest, handleShadowNodeSimpleCloning) {
auto nodeARevision2 =
std::make_shared<TestShadowNode>(*nodeA_, ShadowNodeFragment{});
EXPECT_STREQ(nodeA_->getComponentName(), nodeARevision2->getComponentName());
EXPECT_EQ(nodeA_->getTag(), nodeARevision2->getTag());
EXPECT_EQ(nodeA_->getSurfaceId(), nodeARevision2->getSurfaceId());
EXPECT_EQ(nodeA_->getEventEmitter(), nodeARevision2->getEventEmitter());
}
TEST_F(ShadowNodeTest, handleShadowNodeMutation) {
auto nodeABChildren = nodeAB_->getChildren();
EXPECT_EQ(nodeABChildren.size(), 2);
EXPECT_EQ(nodeABChildren.at(0), nodeABA_);
EXPECT_EQ(nodeABChildren.at(1), nodeABB_);
auto nodeABArevision2 =
std::make_shared<TestShadowNode>(*nodeABA_, ShadowNodeFragment{});
nodeAB_->replaceChild(*nodeABA_, nodeABArevision2);
nodeABChildren = nodeAB_->getChildren();
EXPECT_EQ(nodeABChildren.size(), 2);
EXPECT_EQ(nodeABChildren.at(0), nodeABArevision2);
EXPECT_EQ(nodeABChildren.at(1), nodeABB_);
// Seal the entire tree.
nodeAB_->sealRecursive();
EXPECT_TRUE(nodeAB_->getSealed());
EXPECT_TRUE(nodeABArevision2->getSealed());
EXPECT_TRUE(nodeABB_->getSealed());
}
TEST_F(ShadowNodeTest, handleCloneFunction) {
auto nodeABClone = nodeAB_->clone({});
// Those two nodes are *not* same.
EXPECT_NE(nodeAB_, nodeABClone);
// `secondNodeClone` is an instance of `TestShadowNode`.
EXPECT_NE(
std::dynamic_pointer_cast<const TestShadowNode>(nodeABClone), nullptr);
// Both nodes have same content.
EXPECT_EQ(nodeAB_->getTag(), nodeABClone->getTag());
EXPECT_EQ(nodeAB_->getSurfaceId(), nodeABClone->getSurfaceId());
EXPECT_EQ(nodeAB_->getProps(), nodeABClone->getProps());
}
TEST_F(ShadowNodeTest, handleState) {
auto family = std::make_shared<ShadowNodeFamily>(
ShadowNodeFamilyFragment{
/* .tag = */ 9,
/* .surfaceId = */ surfaceId_,
/* .eventEmitter = */ nullptr,
},
eventDispatcher_,
componentDescriptor_);
auto traits = TestShadowNode::BaseTraits();
auto props = std::make_shared<const TestProps>();
auto fragment = ShadowNodeFragment{
/* .props = */ props,
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
/* .state = */ {}};
auto const initialState =
componentDescriptor_.createInitialState(fragment, family);
auto firstNode = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
/* .props = */ props,
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
/* .state = */ initialState},
family,
traits);
auto secondNode = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
/* .props = */ props,
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
/* .state = */ initialState},
family,
traits);
auto thirdNode = std::make_shared<TestShadowNode>(
ShadowNodeFragment{
/* .props = */ props,
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
/* .state = */ initialState},
family,
traits);
TestShadowNode::ConcreteState::Shared _state =
std::static_pointer_cast<TestShadowNode::ConcreteState const>(
initialState);
_state->updateState(TestState{42});
thirdNode->setStateData({9001});
// State object are compared by pointer, not by value.
EXPECT_EQ(firstNode->getState(), secondNode->getState());
EXPECT_NE(firstNode->getState(), thirdNode->getState());
secondNode->setStateData(TestState{42});
EXPECT_NE(firstNode->getState(), secondNode->getState());
// State cannot be changed for sealed shadow node.
secondNode->sealRecursive();
EXPECT_ANY_THROW(secondNode->setStateData(TestState{42}));
}