123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- /*
- * 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 <memory>
- #include <react/components/root/RootComponentDescriptor.h>
- #include <react/components/view/ViewComponentDescriptor.h>
- #include <react/mounting/Differentiator.h>
- #include <react/mounting/stubs.h>
- #include "shadowTreeGeneration.h"
- #include <Glog/logging.h>
- #include <gtest/gtest.h>
- namespace facebook {
- namespace react {
- static ShadowNode::Shared makeNode(
- ComponentDescriptor const &componentDescriptor,
- int tag,
- ShadowNode::ListOfShared children) {
- auto props = generateDefaultProps(componentDescriptor);
- // Make sure node is layoutable by giving it dimensions and making it
- // accessible This is an implementation detail and subject to change.
- folly::dynamic dynamic = folly::dynamic::object();
- dynamic["position"] = "absolute";
- dynamic["top"] = 0;
- dynamic["left"] = 0;
- dynamic["width"] = 100;
- dynamic["height"] = 100;
- dynamic["nativeId"] = tag;
- dynamic["accessible"] = true;
- auto newProps = componentDescriptor.cloneProps(props, RawProps(dynamic));
- return componentDescriptor.createShadowNode(
- ShadowNodeFragment{newProps,
- std::make_shared<SharedShadowNodeList>(children)},
- componentDescriptor.createFamily({tag, SurfaceId(1), nullptr}, nullptr));
- }
- TEST(MountingTest, testMinimalInstructionGeneration) {
- 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 rootFamily = 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()},
- rootFamily)));
- // Applying size constraints.
- emptyRootNode = emptyRootNode->clone(
- LayoutConstraints{Size{512, 0},
- Size{512, std::numeric_limits<Float>::infinity()}},
- LayoutContext{});
- auto childA = makeNode(viewComponentDescriptor, 100, {});
- auto childB = makeNode(viewComponentDescriptor, 101, {});
- auto childC = makeNode(viewComponentDescriptor, 102, {});
- auto childD = makeNode(viewComponentDescriptor, 103, {});
- auto childE = makeNode(viewComponentDescriptor, 104, {});
- auto childF = makeNode(viewComponentDescriptor, 105, {});
- auto family = viewComponentDescriptor.createFamily(
- {10, SurfaceId(1), nullptr}, nullptr);
- // Construct "identical" shadow nodes: they differ only in children.
- auto shadowNodeV1 = viewComponentDescriptor.createShadowNode(
- ShadowNodeFragment{generateDefaultProps(viewComponentDescriptor),
- std::make_shared<SharedShadowNodeList>(
- SharedShadowNodeList{childB, childC, childD})},
- family);
- auto shadowNodeV2 = shadowNodeV1->clone(ShadowNodeFragment{
- generateDefaultProps(viewComponentDescriptor),
- std::make_shared<SharedShadowNodeList>(
- SharedShadowNodeList{childA, childB, childC, childD})});
- auto shadowNodeV3 = shadowNodeV2->clone(
- ShadowNodeFragment{generateDefaultProps(viewComponentDescriptor),
- std::make_shared<SharedShadowNodeList>(
- SharedShadowNodeList{childB, childC, childD})});
- auto shadowNodeV4 = shadowNodeV3->clone(
- ShadowNodeFragment{generateDefaultProps(viewComponentDescriptor),
- std::make_shared<SharedShadowNodeList>(
- SharedShadowNodeList{childB, childD, childE})});
- auto shadowNodeV5 = shadowNodeV4->clone(ShadowNodeFragment{
- generateDefaultProps(viewComponentDescriptor),
- std::make_shared<SharedShadowNodeList>(
- SharedShadowNodeList{childB, childA, childE, childC})});
- auto shadowNodeV6 = shadowNodeV5->clone(ShadowNodeFragment{
- generateDefaultProps(viewComponentDescriptor),
- std::make_shared<SharedShadowNodeList>(SharedShadowNodeList{
- childB, childA, childD, childF, childE, childC})});
- // Injecting a tree into the root node.
- auto rootNodeV1 = std::static_pointer_cast<RootShadowNode const>(
- emptyRootNode->ShadowNode::clone(
- ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(),
- std::make_shared<SharedShadowNodeList>(
- SharedShadowNodeList{shadowNodeV1})}));
- auto rootNodeV2 = std::static_pointer_cast<RootShadowNode const>(
- rootNodeV1->ShadowNode::clone(
- ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(),
- std::make_shared<SharedShadowNodeList>(
- SharedShadowNodeList{shadowNodeV2})}));
- auto rootNodeV3 = std::static_pointer_cast<RootShadowNode const>(
- rootNodeV2->ShadowNode::clone(
- ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(),
- std::make_shared<SharedShadowNodeList>(
- SharedShadowNodeList{shadowNodeV3})}));
- auto rootNodeV4 = std::static_pointer_cast<RootShadowNode const>(
- rootNodeV3->ShadowNode::clone(
- ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(),
- std::make_shared<SharedShadowNodeList>(
- SharedShadowNodeList{shadowNodeV4})}));
- auto rootNodeV5 = std::static_pointer_cast<RootShadowNode const>(
- rootNodeV4->ShadowNode::clone(
- ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(),
- std::make_shared<SharedShadowNodeList>(
- SharedShadowNodeList{shadowNodeV5})}));
- auto rootNodeV6 = std::static_pointer_cast<RootShadowNode const>(
- rootNodeV5->ShadowNode::clone(
- ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(),
- std::make_shared<SharedShadowNodeList>(
- SharedShadowNodeList{shadowNodeV6})}));
- // Layout and diff
- std::vector<LayoutableShadowNode const *> affectedLayoutableNodesV1{};
- affectedLayoutableNodesV1.reserve(1024);
- std::const_pointer_cast<RootShadowNode>(rootNodeV1)
- ->layoutIfNeeded(&affectedLayoutableNodesV1);
- rootNodeV1->sealRecursive();
- std::vector<LayoutableShadowNode const *> affectedLayoutableNodesV2{};
- affectedLayoutableNodesV2.reserve(1024);
- std::const_pointer_cast<RootShadowNode>(rootNodeV2)
- ->layoutIfNeeded(&affectedLayoutableNodesV2);
- rootNodeV2->sealRecursive();
- std::vector<LayoutableShadowNode const *> affectedLayoutableNodesV3{};
- affectedLayoutableNodesV3.reserve(1024);
- std::const_pointer_cast<RootShadowNode>(rootNodeV3)
- ->layoutIfNeeded(&affectedLayoutableNodesV3);
- rootNodeV3->sealRecursive();
- std::vector<LayoutableShadowNode const *> affectedLayoutableNodesV4{};
- affectedLayoutableNodesV4.reserve(1024);
- std::const_pointer_cast<RootShadowNode>(rootNodeV4)
- ->layoutIfNeeded(&affectedLayoutableNodesV4);
- rootNodeV4->sealRecursive();
- std::vector<LayoutableShadowNode const *> affectedLayoutableNodesV5{};
- affectedLayoutableNodesV5.reserve(1024);
- std::const_pointer_cast<RootShadowNode>(rootNodeV5)
- ->layoutIfNeeded(&affectedLayoutableNodesV5);
- rootNodeV5->sealRecursive();
- std::vector<LayoutableShadowNode const *> affectedLayoutableNodesV6{};
- affectedLayoutableNodesV6.reserve(1024);
- std::const_pointer_cast<RootShadowNode>(rootNodeV6)
- ->layoutIfNeeded(&affectedLayoutableNodesV6);
- rootNodeV6->sealRecursive();
- // This block displays all the mutations for debugging purposes.
- /*
- LOG(ERROR) << "Num mutations: " << mutations.size();
- for (auto const &mutation : mutations) {
- switch (mutation.type) {
- case ShadowViewMutation::Create: {
- LOG(ERROR) << "CREATE " << mutation.newChildShadowView.tag;
- break;
- }
- case ShadowViewMutation::Delete: {
- LOG(ERROR) << "DELETE " << mutation.oldChildShadowView.tag;
- break;
- }
- case ShadowViewMutation::Remove: {
- LOG(ERROR) << "REMOVE " << mutation.oldChildShadowView.tag << " " <<
- mutation.index; break;
- }
- case ShadowViewMutation::Insert: {
- LOG(ERROR) << "INSERT " << mutation.newChildShadowView.tag << " " <<
- mutation.index; break;
- }
- case ShadowViewMutation::Update: {
- LOG(ERROR) << "UPDATE " << mutation.newChildShadowView.tag;
- break;
- }
- }
- }*/
- // Calculating mutations.
- auto mutations1 = calculateShadowViewMutations(
- DifferentiatorMode::OptimizedMoves, *rootNodeV1, *rootNodeV2);
- // The order and exact mutation instructions here may change at any time.
- // This test just ensures that any changes are intentional.
- // This test, in particular, ensures that inserting a node at the beginning
- // produces a single "Insert" instruction, and no remove/insert (move)
- // operations. All these nodes are laid out with absolute positioning, so
- // moving them around does not change layout.
- assert(mutations1.size() == 2);
- assert(mutations1[0].type == ShadowViewMutation::Create);
- assert(mutations1[0].newChildShadowView.tag == 100);
- assert(mutations1[1].type == ShadowViewMutation::Insert);
- assert(mutations1[1].newChildShadowView.tag == 100);
- assert(mutations1[1].index == 0);
- // Calculating mutations.
- auto mutations2 = calculateShadowViewMutations(
- DifferentiatorMode::OptimizedMoves, *rootNodeV2, *rootNodeV3);
- // The order and exact mutation instructions here may change at any time.
- // This test just ensures that any changes are intentional.
- // This test, in particular, ensures that removing a node at the beginning
- // produces a single remove (and delete) instruction, and no remove/insert
- // (move) operations. All these nodes are laid out with absolute positioning,
- // so moving them around does not change layout.
- assert(mutations2.size() == 2);
- assert(mutations2[0].type == ShadowViewMutation::Remove);
- assert(mutations2[0].oldChildShadowView.tag == 100);
- assert(mutations2[0].index == 0);
- assert(mutations2[1].type == ShadowViewMutation::Delete);
- assert(mutations2[1].oldChildShadowView.tag == 100);
- // Calculating mutations.
- auto mutations3 = calculateShadowViewMutations(
- DifferentiatorMode::OptimizedMoves, *rootNodeV3, *rootNodeV4);
- // The order and exact mutation instructions here may change at any time.
- // This test just ensures that any changes are intentional.
- // This test, in particular, ensures that removing a node in the middle
- // produces a single remove (and delete) instruction, and no remove/insert
- // (move) operations; and that simultaneously, we can insert a node at the
- // end. NOTE: This list of mutations has some unexpected "Update"
- // instructions, due to layout issues (some LayoutMetrics are 0). Not sure
- // why, but the point of this test is to make sure there aren't unnecessary
- // insert/deletes, so we can ignore for now.
- assert(mutations3.size() == 7);
- assert(mutations3[0].type == ShadowViewMutation::Update);
- assert(mutations3[1].type == ShadowViewMutation::Update);
- assert(mutations3[2].type == ShadowViewMutation::Update);
- assert(mutations3[3].type == ShadowViewMutation::Remove);
- assert(mutations3[3].oldChildShadowView.tag == 102);
- assert(mutations3[3].index == 1);
- assert(mutations3[4].type == ShadowViewMutation::Delete);
- assert(mutations3[4].oldChildShadowView.tag == 102);
- assert(mutations3[5].type == ShadowViewMutation::Create);
- assert(mutations3[5].newChildShadowView.tag == 104);
- assert(mutations3[6].type == ShadowViewMutation::Insert);
- assert(mutations3[6].newChildShadowView.tag == 104);
- assert(mutations3[6].index == 2);
- // Calculating mutations.
- auto mutations4 = calculateShadowViewMutations(
- DifferentiatorMode::OptimizedMoves, *rootNodeV4, *rootNodeV5);
- // The order and exact mutation instructions here may change at any time.
- // This test just ensures that any changes are intentional.
- // This test, in particular, ensures that inserting a child at the middle, and
- // at the end, and removing a node in the middle, produces the minimal set of
- // instructions. All these nodes are laid out with absolute positioning, so
- // moving them around does not change layout. NOTE: This list of mutations has
- // some unexpected "Update" instructions, due to layout issues (some
- // LayoutMetrics are 0). Not sure why, but the point of this test is to make
- // sure there aren't unnecessary insert/deletes, so we can ignore for now.
- assert(mutations4.size() == 9);
- assert(mutations4[0].type == ShadowViewMutation::Update);
- assert(mutations4[1].type == ShadowViewMutation::Update);
- assert(mutations4[2].type == ShadowViewMutation::Update);
- assert(mutations4[3].type == ShadowViewMutation::Remove);
- assert(mutations4[3].oldChildShadowView.tag == 103);
- assert(mutations4[3].index == 1);
- assert(mutations4[4].type == ShadowViewMutation::Delete);
- assert(mutations4[4].oldChildShadowView.tag == 103);
- assert(mutations4[5].type == ShadowViewMutation::Create);
- assert(mutations4[5].newChildShadowView.tag == 100);
- assert(mutations4[6].type == ShadowViewMutation::Create);
- assert(mutations4[6].newChildShadowView.tag == 102);
- assert(mutations4[7].type == ShadowViewMutation::Insert);
- assert(mutations4[7].newChildShadowView.tag == 100);
- assert(mutations4[7].index == 1);
- assert(mutations4[8].type == ShadowViewMutation::Insert);
- assert(mutations4[8].newChildShadowView.tag == 102);
- assert(mutations4[8].index == 3);
- auto mutations5 = calculateShadowViewMutations(
- DifferentiatorMode::OptimizedMoves, *rootNodeV5, *rootNodeV6);
- // The order and exact mutation instructions here may change at any time.
- // This test just ensures that any changes are intentional.
- // This test, in particular, ensures that inserting TWO children in the middle
- // produces the minimal set of instructions. All these nodes are laid out with
- // absolute positioning, so moving them around does not change layout.
- assert(mutations5.size() == 4);
- assert(mutations5[0].type == ShadowViewMutation::Create);
- assert(mutations5[0].newChildShadowView.tag == 103);
- assert(mutations5[1].type == ShadowViewMutation::Create);
- assert(mutations5[1].newChildShadowView.tag == 105);
- assert(mutations5[2].type == ShadowViewMutation::Insert);
- assert(mutations5[2].newChildShadowView.tag == 103);
- assert(mutations5[2].index == 2);
- assert(mutations5[3].type == ShadowViewMutation::Insert);
- assert(mutations5[3].newChildShadowView.tag == 105);
- assert(mutations5[3].index == 3);
- }
- } // namespace react
- } // namespace facebook
|