diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/LazyShadowTreeRevisionConsistencyManager.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/LazyShadowTreeRevisionConsistencyManager.cpp index c195a9b9136916..53e91ce7b953e2 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/LazyShadowTreeRevisionConsistencyManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/LazyShadowTreeRevisionConsistencyManager.cpp @@ -18,8 +18,7 @@ LazyShadowTreeRevisionConsistencyManager:: void LazyShadowTreeRevisionConsistencyManager::updateCurrentRevision( SurfaceId surfaceId, RootShadowNode::Shared rootShadowNode) { - capturedRootShadowNodesForConsistency_.emplace( - surfaceId, std::move(rootShadowNode)); + capturedRootShadowNodesForConsistency_[surfaceId] = std::move(rootShadowNode); } #pragma mark - ShadowTreeRevisionProvider @@ -38,7 +37,7 @@ LazyShadowTreeRevisionConsistencyManager::getCurrentRevision( rootShadowNode = shadowTree.getCurrentRevision().rootShadowNode; }); - capturedRootShadowNodesForConsistency_.emplace(surfaceId, rootShadowNode); + capturedRootShadowNodesForConsistency_[surfaceId] = rootShadowNode; return rootShadowNode; } diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/tests/LazyShadowTreeRevisionConsistencyManagerTest.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/tests/LazyShadowTreeRevisionConsistencyManagerTest.cpp new file mode 100644 index 00000000000000..ad7aabba4bb6a5 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/tests/LazyShadowTreeRevisionConsistencyManagerTest.cpp @@ -0,0 +1,304 @@ +/* + * 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 +#include +#include +#include +#include +#include + +namespace facebook::react { + +class FakeShadowTreeDelegate : 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 {}; +}; + +class LazyShadowTreeRevisionConsistencyManagerTest : public ::testing::Test { + public: + LazyShadowTreeRevisionConsistencyManagerTest() + : consistencyManager_(shadowTreeRegistry_) {} + + void TearDown() override { + // this is necessary because otherwise the test will crash with an assertion + // preventing the deallocation of the registry with registered shadow trees. + auto ids = std::vector(); + + shadowTreeRegistry_.enumerate( + [&ids](const ShadowTree& shadowTree, bool& /*stop*/) { + ids.push_back(shadowTree.getSurfaceId()); + }); + + for (auto id : ids) { + shadowTreeRegistry_.remove(id); + } + } + + std::unique_ptr createShadowTree(SurfaceId surfaceId) { + return std::make_unique( + surfaceId, + layoutConstraints_, + layoutContext_, + shadowTreeDelegate_, + contextContainer_); + } + + ShadowTreeRegistry shadowTreeRegistry_{}; + LazyShadowTreeRevisionConsistencyManager consistencyManager_; + + LayoutConstraints layoutConstraints_{}; + LayoutContext layoutContext_{}; + FakeShadowTreeDelegate shadowTreeDelegate_{}; + ContextContainer contextContainer_{}; +}; + +TEST_F(LazyShadowTreeRevisionConsistencyManagerTest, testLockedOnNoRevision) { + consistencyManager_.lockRevisions(); + + EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr); + + shadowTreeRegistry_.add(createShadowTree(0)); + + EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr); + + auto element = Element(); + auto builder = simpleComponentBuilder(); + auto newRootShadowNode = builder.build(element); + + shadowTreeRegistry_.visit( + 0, [newRootShadowNode](const ShadowTree& shadowTree) { + shadowTree.commit( + [&](const RootShadowNode& /*oldRootShadowNode*/) { + return newRootShadowNode; + }, + {}); + }); + + EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr); + + consistencyManager_.unlockRevisions(); +} + +TEST_F( + LazyShadowTreeRevisionConsistencyManagerTest, + testLockedOnNoRevisionWithUpdate) { + consistencyManager_.lockRevisions(); + + EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr); + + shadowTreeRegistry_.add(createShadowTree(0)); + + EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr); + + auto element = Element(); + auto builder = simpleComponentBuilder(); + auto newRootShadowNode = builder.build(element); + + shadowTreeRegistry_.visit( + 0, [newRootShadowNode](const ShadowTree& shadowTree) { + shadowTree.commit( + [&](const RootShadowNode& /*oldRootShadowNode*/) { + return newRootShadowNode; + }, + {}); + }); + + EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr); + + consistencyManager_.updateCurrentRevision(0, newRootShadowNode); + + EXPECT_NE(consistencyManager_.getCurrentRevision(0), nullptr); + EXPECT_EQ( + consistencyManager_.getCurrentRevision(0).get(), newRootShadowNode.get()); + + consistencyManager_.unlockRevisions(); +} + +TEST_F( + LazyShadowTreeRevisionConsistencyManagerTest, + testLockedOnNoRevisionWithMultipleUpdates) { + consistencyManager_.lockRevisions(); + + EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr); + + shadowTreeRegistry_.add(createShadowTree(0)); + + EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr); + + auto element = Element(); + auto builder = simpleComponentBuilder(); + auto newRootShadowNode = builder.build(element); + + shadowTreeRegistry_.visit( + 0, [newRootShadowNode](const ShadowTree& shadowTree) { + shadowTree.commit( + [&](const RootShadowNode& /*oldRootShadowNode*/) { + return newRootShadowNode; + }, + {}); + }); + + EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr); + + consistencyManager_.updateCurrentRevision(0, newRootShadowNode); + + EXPECT_EQ( + consistencyManager_.getCurrentRevision(0).get(), newRootShadowNode.get()); + + auto newRootShadowNode2 = builder.build(element); + + shadowTreeRegistry_.visit( + 0, [newRootShadowNode2](const ShadowTree& shadowTree) { + shadowTree.commit( + [&](const RootShadowNode& /*oldRootShadowNode*/) { + return newRootShadowNode2; + }, + {}); + }); + + consistencyManager_.updateCurrentRevision(0, newRootShadowNode2); + + EXPECT_EQ( + consistencyManager_.getCurrentRevision(0).get(), + newRootShadowNode2.get()); + + consistencyManager_.unlockRevisions(); +} + +TEST_F( + LazyShadowTreeRevisionConsistencyManagerTest, + testLockedOnExistingRevision) { + shadowTreeRegistry_.add(createShadowTree(0)); + + auto element = Element(); + auto builder = simpleComponentBuilder(); + auto newRootShadowNode = builder.build(element); + + shadowTreeRegistry_.visit( + 0, [newRootShadowNode](const ShadowTree& shadowTree) { + shadowTree.commit( + [&](const RootShadowNode& /*oldRootShadowNode*/) { + return newRootShadowNode; + }, + {}); + }); + + consistencyManager_.lockRevisions(); + + EXPECT_EQ( + consistencyManager_.getCurrentRevision(0).get(), newRootShadowNode.get()); + + consistencyManager_.unlockRevisions(); +} + +TEST_F( + LazyShadowTreeRevisionConsistencyManagerTest, + testLockedOnExistingRevisionWithUpdates) { + shadowTreeRegistry_.add(createShadowTree(0)); + + auto element = Element(); + auto builder = simpleComponentBuilder(); + auto newRootShadowNode = builder.build(element); + + shadowTreeRegistry_.visit( + 0, [newRootShadowNode](const ShadowTree& shadowTree) { + shadowTree.commit( + [&](const RootShadowNode& /*oldRootShadowNode*/) { + return newRootShadowNode; + }, + {}); + }); + + consistencyManager_.lockRevisions(); + + EXPECT_EQ( + consistencyManager_.getCurrentRevision(0).get(), newRootShadowNode.get()); + + auto newRootShadowNode2 = builder.build(element); + + shadowTreeRegistry_.visit( + 0, [newRootShadowNode2](const ShadowTree& shadowTree) { + shadowTree.commit( + [&](const RootShadowNode& /*oldRootShadowNode*/) { + return newRootShadowNode2; + }, + {}); + }); + + // Not updated + EXPECT_EQ( + consistencyManager_.getCurrentRevision(0).get(), newRootShadowNode.get()); + + consistencyManager_.updateCurrentRevision(0, newRootShadowNode2); + + // Updated + EXPECT_EQ( + consistencyManager_.getCurrentRevision(0).get(), + newRootShadowNode2.get()); + + consistencyManager_.unlockRevisions(); +} + +TEST_F(LazyShadowTreeRevisionConsistencyManagerTest, testLockAfterUnlock) { + shadowTreeRegistry_.add(createShadowTree(0)); + + auto element = Element(); + auto builder = simpleComponentBuilder(); + auto newRootShadowNode = builder.build(element); + + shadowTreeRegistry_.visit( + 0, [newRootShadowNode](const ShadowTree& shadowTree) { + shadowTree.commit( + [&](const RootShadowNode& /*oldRootShadowNode*/) { + return newRootShadowNode; + }, + {}); + }); + + consistencyManager_.lockRevisions(); + + EXPECT_EQ( + consistencyManager_.getCurrentRevision(0).get(), newRootShadowNode.get()); + + auto newRootShadowNode2 = builder.build(element); + + shadowTreeRegistry_.visit( + 0, [newRootShadowNode2](const ShadowTree& shadowTree) { + shadowTree.commit( + [&](const RootShadowNode& /*oldRootShadowNode*/) { + return newRootShadowNode2; + }, + {}); + }); + + // Not updated + EXPECT_EQ( + consistencyManager_.getCurrentRevision(0).get(), newRootShadowNode.get()); + + consistencyManager_.unlockRevisions(); + + consistencyManager_.lockRevisions(); + + // Updated + EXPECT_EQ( + consistencyManager_.getCurrentRevision(0).get(), + newRootShadowNode2.get()); + + consistencyManager_.unlockRevisions(); +} + +} // namespace facebook::react