Add test for LazyShadowTreeRevisionConsistencyManager and fix bugs

Summary:
Changelog: [internal]

`LazyShadowTreeRevisionConsistencyManager` wasn't correctly updating the locked revision, because `emplace` is a no-op if there's already a value for the key in the `unordered_map`.

This fixes the issue and adds tests that actually showed it.

Reviewed By: sammy-SC

Differential Revision: D56761941

fbshipit-source-id: 340e9195b14460a591c48186bd365688c74ade04
This commit is contained in:
Rubén Norte
2024-04-30 10:29:22 -07:00
committed by Facebook GitHub Bot
parent 9cfc71dc66
commit cfbee0b7ec
2 changed files with 306 additions and 3 deletions
@@ -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;
}
@@ -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 <gtest/gtest.h>
#include <react/renderer/components/root/RootShadowNode.h>
#include <react/renderer/element/testUtils.h>
#include <react/renderer/mounting/ShadowTree.h>
#include <react/renderer/mounting/ShadowTreeRegistry.h>
#include <react/renderer/uimanager/consistency/LazyShadowTreeRevisionConsistencyManager.h>
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<SurfaceId>();
shadowTreeRegistry_.enumerate(
[&ids](const ShadowTree& shadowTree, bool& /*stop*/) {
ids.push_back(shadowTree.getSurfaceId());
});
for (auto id : ids) {
shadowTreeRegistry_.remove(id);
}
}
std::unique_ptr<ShadowTree> createShadowTree(SurfaceId surfaceId) {
return std::make_unique<ShadowTree>(
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<RootShadowNode>();
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<RootShadowNode>();
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<RootShadowNode>();
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<RootShadowNode>();
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<RootShadowNode>();
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<RootShadowNode>();
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