Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add test for LazyShadowTreeRevisionConsistencyManager and fix bugs #44337

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -38,7 +37,7 @@ LazyShadowTreeRevisionConsistencyManager::getCurrentRevision(
rootShadowNode = shadowTree.getCurrentRevision().rootShadowNode;
});

capturedRootShadowNodesForConsistency_.emplace(surfaceId, rootShadowNode);
capturedRootShadowNodesForConsistency_[surfaceId] = rootShadowNode;

return rootShadowNode;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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