Files
react-native/ReactCommon/react/renderer/mounting/tests/ShadowTreeLifeCycleTest.cpp
T
Joshua Gross 39b8233c93 Copy Differ implementation to new file, feature-flag-gate new differ
Summary:
Changes in following diffs will be gated by this feature flag.

The differ in the new file is copied from the current stable implementation and will not be modified until it's deleted.

Changelog: [Internal]

Reviewed By: sammy-SC, mdvacca

Differential Revision: D27775698

fbshipit-source-id: 03d9518ffd2b1f25712386c56a38bd2b4d839fc2
2021-04-14 19:50:09 -07:00

206 lines
6.3 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 <vector>
#include <glog/logging.h>
#include <gtest/gtest.h>
#include <react/renderer/components/root/RootComponentDescriptor.h>
#include <react/renderer/components/view/ViewComponentDescriptor.h>
#include <react/renderer/mounting/Differentiator.h>
#include <react/renderer/mounting/ShadowViewMutation.h>
#include <react/renderer/mounting/stubs.h>
#include "Entropy.h"
#include "shadowTreeGeneration.h"
namespace facebook {
namespace react {
static void testShadowNodeTreeLifeCycle(
uint_fast32_t seed,
int treeSize,
int repeats,
int stages) {
auto entropy = seed == 0 ? Entropy() : Entropy(seed);
auto eventDispatcher = EventDispatcher::Shared{};
auto contextContainer = std::make_shared<ContextContainer>();
auto componentDescriptorParameters =
ComponentDescriptorParameters{eventDispatcher, contextContainer, nullptr};
auto viewComponentDescriptor =
ViewComponentDescriptor(componentDescriptorParameters);
auto rootComponentDescriptor =
RootComponentDescriptor(componentDescriptorParameters);
auto noopEventEmitter =
std::make_shared<ViewEventEmitter const>(nullptr, -1, eventDispatcher);
auto allNodes = std::vector<ShadowNode::Shared>{};
for (int i = 0; i < repeats; i++) {
allNodes.clear();
auto family = rootComponentDescriptor.createFamily(
{Tag(1), SurfaceId(1), nullptr}, nullptr);
// Creating an initial root shadow node.
auto emptyRootNode = std::const_pointer_cast<RootShadowNode>(
std::static_pointer_cast<RootShadowNode const>(
rootComponentDescriptor.createShadowNode(
ShadowNodeFragment{RootShadowNode::defaultSharedProps()},
family)));
// Applying size constraints.
emptyRootNode = emptyRootNode->clone(
LayoutConstraints{
Size{512, 0}, Size{512, std::numeric_limits<Float>::infinity()}},
LayoutContext{});
// Generation of a random tree.
auto singleRootChildNode =
generateShadowNodeTree(entropy, viewComponentDescriptor, treeSize);
// Injecting a tree into the root node.
auto currentRootNode = std::static_pointer_cast<RootShadowNode const>(
emptyRootNode->ShadowNode::clone(ShadowNodeFragment{
ShadowNodeFragment::propsPlaceholder(),
std::make_shared<SharedShadowNodeList>(
SharedShadowNodeList{singleRootChildNode})}));
// Building an initial view hierarchy.
auto viewTree = buildStubViewTreeWithoutUsingDifferentiator(*emptyRootNode);
viewTree.mutate(
calculateShadowViewMutations(*emptyRootNode, *currentRootNode, true));
for (int j = 0; j < stages; j++) {
auto nextRootNode = currentRootNode;
// Mutating the tree.
alterShadowTree(
entropy,
nextRootNode,
{
&messWithChildren,
&messWithYogaStyles,
&messWithLayotableOnlyFlag,
});
std::vector<LayoutableShadowNode const *> affectedLayoutableNodes{};
affectedLayoutableNodes.reserve(1024);
// Laying out the tree.
std::const_pointer_cast<RootShadowNode>(nextRootNode)
->layoutIfNeeded(&affectedLayoutableNodes);
nextRootNode->sealRecursive();
allNodes.push_back(nextRootNode);
// Calculating mutations.
auto mutations =
calculateShadowViewMutations(*currentRootNode, *nextRootNode, true);
// Make sure that in a single frame, a DELETE for a
// view is not followed by a CREATE for the same view.
{
std::vector<int> deletedTags{};
for (auto const &mutation : mutations) {
if (mutation.type == ShadowViewMutation::Type::Delete) {
deletedTags.push_back(mutation.oldChildShadowView.tag);
}
}
for (auto const &mutation : mutations) {
if (mutation.type == ShadowViewMutation::Type::Create) {
if (std::find(
deletedTags.begin(),
deletedTags.end(),
mutation.newChildShadowView.tag) != deletedTags.end()) {
LOG(ERROR) << "Deleted tag was recreated in mutations list: ["
<< mutation.newChildShadowView.tag << "]";
FAIL();
}
}
}
}
// Mutating the view tree.
viewTree.mutate(mutations);
// Building a view tree to compare with.
auto rebuiltViewTree =
buildStubViewTreeWithoutUsingDifferentiator(*nextRootNode);
// Comparing the newly built tree with the updated one.
if (rebuiltViewTree != viewTree) {
// Something went wrong.
LOG(ERROR) << "Entropy seed: " << entropy.getSeed() << "\n";
// There are some issues getting `getDebugDescription` to compile
// under test on Android for now.
#ifndef ANDROID
LOG(ERROR) << "Shadow Tree before: \n"
<< currentRootNode->getDebugDescription();
LOG(ERROR) << "Shadow Tree after: \n"
<< nextRootNode->getDebugDescription();
LOG(ERROR) << "View Tree before: \n"
<< getDebugDescription(viewTree.getRootStubView(), {});
LOG(ERROR) << "View Tree after: \n"
<< getDebugDescription(
rebuiltViewTree.getRootStubView(), {});
LOG(ERROR) << "Mutations:"
<< "\n"
<< getDebugDescription(mutations, {});
#endif
FAIL();
}
currentRootNode = nextRootNode;
}
}
SUCCEED();
}
} // namespace react
} // namespace facebook
using namespace facebook::react;
TEST(
ShadowTreeLifecyleTest,
stableBiggerTreeFewerIterationsOptimizedMovesFlattener) {
testShadowNodeTreeLifeCycle(
/* seed */ 0,
/* size */ 512,
/* repeats */ 32,
/* stages */ 32);
}
TEST(
ShadowTreeLifecyleTest,
stableBiggerTreeFewerIterationsOptimizedMovesFlattener2) {
testShadowNodeTreeLifeCycle(
/* seed */ 1,
/* size */ 512,
/* repeats */ 32,
/* stages */ 32);
}
TEST(
ShadowTreeLifecyleTest,
stableSmallerTreeMoreIterationsOptimizedMovesFlattener) {
testShadowNodeTreeLifeCycle(
/* seed */ 0,
/* size */ 16,
/* repeats */ 512,
/* stages */ 32);
}