guardia-messenger/node_modules/react-native/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp
DESKTOP-TKLFCPRython f29f525c77 refactor: 101.79.17.164 → zioinfo.co.kr 전체 도메인 변환 + Manager UI 배포
- 37개 파일 IP → zioinfo.co.kr 치환 (소스/매뉴얼/설정/하네스)
- Manager DrConsole/NetworkConsole/CsapConsole 빌드 + /var/www/manager/ 배포
- 테스트: Manager HTTP 200, ITSM 신규 API 7개 전체 200

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 10:09:17 +09:00

302 lines
9.6 KiB
C++

/*
* 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 <memory>
#include <gtest/gtest.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <react/renderer/components/view/ViewComponentDescriptor.h>
#include <react/renderer/core/PropsParserContext.h>
#include <react/renderer/element/ComponentBuilder.h>
#include <react/renderer/element/Element.h>
#include <react/renderer/mounting/MountingCoordinator.h>
#include <react/renderer/mounting/ShadowTree.h>
#include <react/renderer/mounting/ShadowTreeDelegate.h>
#include <react/renderer/element/testUtils.h>
using namespace facebook::react;
class DummyShadowTreeDelegate : public ShadowTreeDelegate {
public:
RootShadowNode::Unshared shadowTreeWillCommit(
const ShadowTree& /*shadowTree*/,
const RootShadowNode::Shared& /*oldRootShadowNode*/,
const RootShadowNode::Unshared& newRootShadowNode) const override {
return newRootShadowNode;
};
void shadowTreeDidFinishTransaction(
MountingCoordinator::Shared mountingCoordinator,
bool mountSynchronously) const override{};
};
namespace {
const ShadowNode* findDescendantNode(
const ShadowNode& shadowNode,
const ShadowNodeFamily& family) {
if (&shadowNode.getFamily() == &family) {
return &shadowNode;
}
for (auto childNode : shadowNode.getChildren()) {
auto descendant = findDescendantNode(*childNode, family);
if (descendant != nullptr) {
return descendant;
}
}
return nullptr;
}
const ShadowNode* findDescendantNode(
const ShadowTree& shadowTree,
const ShadowNodeFamily& family) {
return findDescendantNode(
*shadowTree.getCurrentRevision().rootShadowNode, family);
}
} // namespace
class StateReconciliationTest : public ::testing::TestWithParam<bool> {
public:
StateReconciliationTest() : builder_(simpleComponentBuilder()) {
CoreFeatures::enableClonelessStateProgression = GetParam();
}
ComponentBuilder builder_;
};
TEST_P(StateReconciliationTest, testStateReconciliation) {
auto shadowNodeA = std::shared_ptr<RootShadowNode>{};
auto shadowNodeAA = std::shared_ptr<ViewShadowNode>{};
auto shadowNodeAB = std::shared_ptr<ScrollViewShadowNode>{};
// clang-format off
auto element =
Element<RootShadowNode>()
.reference(shadowNodeA)
.children({
Element<ViewShadowNode>()
.reference(shadowNodeAA),
Element<ScrollViewShadowNode>()
.reference(shadowNodeAB)
});
// clang-format on
ContextContainer contextContainer{};
auto shadowNode = builder_.build(element);
auto rootShadowNodeState1 = shadowNode->ShadowNode::clone({});
auto& scrollViewComponentDescriptor = shadowNodeAB->getComponentDescriptor();
auto& family = shadowNodeAB->getFamily();
auto state1 = shadowNodeAB->getState();
auto shadowTreeDelegate = DummyShadowTreeDelegate{};
ShadowTree shadowTree{
SurfaceId{11},
LayoutConstraints{},
LayoutContext{},
shadowTreeDelegate,
contextContainer};
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return std::static_pointer_cast<RootShadowNode>(rootShadowNodeState1);
},
{true});
EXPECT_EQ(state1->getMostRecentState(), state1);
EXPECT_EQ(
findDescendantNode(*rootShadowNodeState1, family)->getState(), state1);
auto state2 = scrollViewComponentDescriptor.createState(
family, std::make_shared<const ScrollViewState>());
auto rootShadowNodeState2 =
shadowNode->cloneTree(family, [&](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone(
{ShadowNodeFragment::propsPlaceholder(),
ShadowNodeFragment::childrenPlaceholder(),
state2});
});
EXPECT_EQ(
findDescendantNode(*rootShadowNodeState2, family)->getState(), state2);
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return std::static_pointer_cast<RootShadowNode>(rootShadowNodeState2);
},
{true});
EXPECT_EQ(state1->getMostRecentState(), state2);
EXPECT_EQ(state2->getMostRecentState(), state2);
auto state3 = scrollViewComponentDescriptor.createState(
family, std::make_shared<const ScrollViewState>());
auto rootShadowNodeState3 = rootShadowNodeState2->cloneTree(
family, [&](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone(
{ShadowNodeFragment::propsPlaceholder(),
ShadowNodeFragment::childrenPlaceholder(),
state3});
});
EXPECT_EQ(
findDescendantNode(*rootShadowNodeState3, family)->getState(), state3);
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return std::static_pointer_cast<RootShadowNode>(rootShadowNodeState3);
},
{true});
EXPECT_EQ(findDescendantNode(shadowTree, family)->getState(), state3);
EXPECT_EQ(state1->getMostRecentState(), state3);
EXPECT_EQ(state2->getMostRecentState(), state3);
EXPECT_EQ(state3->getMostRecentState(), state3);
// This is the core part of the whole test.
// Here we commit the old tree but we expect that the state associated with
// the node will stay the same (newer that the old tree has).
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return std::static_pointer_cast<RootShadowNode>(rootShadowNodeState2);
},
{true});
// Warning:
// there is important semantic difference with the approach. With the old
// algorithm, you couldn't go back to a shadow node with old state. New state
// was always enforced when state reconciliation was enabled. The clone-less
// algorithm does not support that, because it can't mutate such a node in
// place.
if (!GetParam()) {
EXPECT_EQ(findDescendantNode(shadowTree, family)->getState(), state3);
} else {
EXPECT_EQ(findDescendantNode(shadowTree, family)->getState(), state2);
}
}
TEST_P(StateReconciliationTest, testCloneslessStateReconciliationDoesntClone) {
auto shadowNodeA = std::shared_ptr<RootShadowNode>{};
auto shadowNodeAA = std::shared_ptr<ViewShadowNode>{};
auto shadowNodeAB = std::shared_ptr<ScrollViewShadowNode>{};
// clang-format off
auto element =
Element<RootShadowNode>()
.reference(shadowNodeA)
.children({
Element<ViewShadowNode>()
.reference(shadowNodeAA),
Element<ScrollViewShadowNode>()
.reference(shadowNodeAB)
});
// clang-format on
ContextContainer contextContainer{};
auto rootShadowNode1 = builder_.build(element);
auto& scrollViewComponentDescriptor = shadowNodeAB->getComponentDescriptor();
auto& family = shadowNodeAB->getFamily();
auto state1 = shadowNodeAB->getState();
auto shadowTreeDelegate = DummyShadowTreeDelegate{};
ShadowTree shadowTree{
SurfaceId{11},
LayoutConstraints{},
LayoutContext{},
shadowTreeDelegate,
contextContainer};
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return std::static_pointer_cast<RootShadowNode>(rootShadowNode1);
},
{true});
EXPECT_EQ(state1->getMostRecentState(), state1);
EXPECT_EQ(findDescendantNode(*rootShadowNode1, family)->getState(), state1);
auto state2 = scrollViewComponentDescriptor.createState(
family, std::make_shared<const ScrollViewState>());
auto rootShadowNode2 =
rootShadowNode1->cloneTree(family, [&](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone(
{ShadowNodeFragment::propsPlaceholder(),
ShadowNodeFragment::childrenPlaceholder(),
state2});
});
EXPECT_EQ(findDescendantNode(*rootShadowNode2, family)->getState(), state2);
EXPECT_EQ(state1->getMostRecentState(), state1);
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return std::static_pointer_cast<RootShadowNode>(rootShadowNode2);
},
{true});
EXPECT_EQ(state1->getMostRecentState(), state2);
EXPECT_EQ(state2->getMostRecentState(), state2);
ShadowNode::Unshared newlyClonedShadowNode;
auto rootShadowNodeClonedFromReact =
rootShadowNode2->cloneTree(family, [&](const ShadowNode& oldShadowNode) {
newlyClonedShadowNode = oldShadowNode.clone({});
return newlyClonedShadowNode;
});
auto state3 = scrollViewComponentDescriptor.createState(
family, std::make_shared<const ScrollViewState>());
auto rootShadowNodeClonedFromStateUpdate =
rootShadowNode2->cloneTree(family, [&](const ShadowNode& oldShadowNode) {
return oldShadowNode.clone(
{ShadowNodeFragment::propsPlaceholder(),
ShadowNodeFragment::childrenPlaceholder(),
state3});
});
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return std::static_pointer_cast<RootShadowNode>(
rootShadowNodeClonedFromStateUpdate);
},
{});
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return std::static_pointer_cast<RootShadowNode>(
rootShadowNodeClonedFromReact);
},
{true});
auto scrollViewShadowNode = findDescendantNode(shadowTree, family);
EXPECT_EQ(scrollViewShadowNode->getState(), state3);
if (GetParam()) {
// Checking that newlyClonedShadowNode was not cloned unnecessarly by state
// progression. This fails with the old algorithm.
EXPECT_EQ(scrollViewShadowNode, newlyClonedShadowNode.get());
}
}
INSTANTIATE_TEST_SUITE_P(
StateReconciliationTestInstantiation,
StateReconciliationTest,
testing::Values(false, true));