MountingTest.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #include <memory>
  8. #include <react/components/root/RootComponentDescriptor.h>
  9. #include <react/components/view/ViewComponentDescriptor.h>
  10. #include <react/mounting/Differentiator.h>
  11. #include <react/mounting/stubs.h>
  12. #include "shadowTreeGeneration.h"
  13. #include <Glog/logging.h>
  14. #include <gtest/gtest.h>
  15. namespace facebook {
  16. namespace react {
  17. static ShadowNode::Shared makeNode(
  18. ComponentDescriptor const &componentDescriptor,
  19. int tag,
  20. ShadowNode::ListOfShared children) {
  21. auto props = generateDefaultProps(componentDescriptor);
  22. // Make sure node is layoutable by giving it dimensions and making it
  23. // accessible This is an implementation detail and subject to change.
  24. folly::dynamic dynamic = folly::dynamic::object();
  25. dynamic["position"] = "absolute";
  26. dynamic["top"] = 0;
  27. dynamic["left"] = 0;
  28. dynamic["width"] = 100;
  29. dynamic["height"] = 100;
  30. dynamic["nativeId"] = tag;
  31. dynamic["accessible"] = true;
  32. auto newProps = componentDescriptor.cloneProps(props, RawProps(dynamic));
  33. return componentDescriptor.createShadowNode(
  34. ShadowNodeFragment{newProps,
  35. std::make_shared<SharedShadowNodeList>(children)},
  36. componentDescriptor.createFamily({tag, SurfaceId(1), nullptr}, nullptr));
  37. }
  38. TEST(MountingTest, testMinimalInstructionGeneration) {
  39. auto eventDispatcher = EventDispatcher::Shared{};
  40. auto contextContainer = std::make_shared<ContextContainer>();
  41. auto componentDescriptorParameters =
  42. ComponentDescriptorParameters{eventDispatcher, contextContainer, nullptr};
  43. auto viewComponentDescriptor =
  44. ViewComponentDescriptor(componentDescriptorParameters);
  45. auto rootComponentDescriptor =
  46. RootComponentDescriptor(componentDescriptorParameters);
  47. auto rootFamily = rootComponentDescriptor.createFamily(
  48. {Tag(1), SurfaceId(1), nullptr}, nullptr);
  49. // Creating an initial root shadow node.
  50. auto emptyRootNode = std::const_pointer_cast<RootShadowNode>(
  51. std::static_pointer_cast<RootShadowNode const>(
  52. rootComponentDescriptor.createShadowNode(
  53. ShadowNodeFragment{RootShadowNode::defaultSharedProps()},
  54. rootFamily)));
  55. // Applying size constraints.
  56. emptyRootNode = emptyRootNode->clone(
  57. LayoutConstraints{Size{512, 0},
  58. Size{512, std::numeric_limits<Float>::infinity()}},
  59. LayoutContext{});
  60. auto childA = makeNode(viewComponentDescriptor, 100, {});
  61. auto childB = makeNode(viewComponentDescriptor, 101, {});
  62. auto childC = makeNode(viewComponentDescriptor, 102, {});
  63. auto childD = makeNode(viewComponentDescriptor, 103, {});
  64. auto childE = makeNode(viewComponentDescriptor, 104, {});
  65. auto childF = makeNode(viewComponentDescriptor, 105, {});
  66. auto family = viewComponentDescriptor.createFamily(
  67. {10, SurfaceId(1), nullptr}, nullptr);
  68. // Construct "identical" shadow nodes: they differ only in children.
  69. auto shadowNodeV1 = viewComponentDescriptor.createShadowNode(
  70. ShadowNodeFragment{generateDefaultProps(viewComponentDescriptor),
  71. std::make_shared<SharedShadowNodeList>(
  72. SharedShadowNodeList{childB, childC, childD})},
  73. family);
  74. auto shadowNodeV2 = shadowNodeV1->clone(ShadowNodeFragment{
  75. generateDefaultProps(viewComponentDescriptor),
  76. std::make_shared<SharedShadowNodeList>(
  77. SharedShadowNodeList{childA, childB, childC, childD})});
  78. auto shadowNodeV3 = shadowNodeV2->clone(
  79. ShadowNodeFragment{generateDefaultProps(viewComponentDescriptor),
  80. std::make_shared<SharedShadowNodeList>(
  81. SharedShadowNodeList{childB, childC, childD})});
  82. auto shadowNodeV4 = shadowNodeV3->clone(
  83. ShadowNodeFragment{generateDefaultProps(viewComponentDescriptor),
  84. std::make_shared<SharedShadowNodeList>(
  85. SharedShadowNodeList{childB, childD, childE})});
  86. auto shadowNodeV5 = shadowNodeV4->clone(ShadowNodeFragment{
  87. generateDefaultProps(viewComponentDescriptor),
  88. std::make_shared<SharedShadowNodeList>(
  89. SharedShadowNodeList{childB, childA, childE, childC})});
  90. auto shadowNodeV6 = shadowNodeV5->clone(ShadowNodeFragment{
  91. generateDefaultProps(viewComponentDescriptor),
  92. std::make_shared<SharedShadowNodeList>(SharedShadowNodeList{
  93. childB, childA, childD, childF, childE, childC})});
  94. // Injecting a tree into the root node.
  95. auto rootNodeV1 = std::static_pointer_cast<RootShadowNode const>(
  96. emptyRootNode->ShadowNode::clone(
  97. ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(),
  98. std::make_shared<SharedShadowNodeList>(
  99. SharedShadowNodeList{shadowNodeV1})}));
  100. auto rootNodeV2 = std::static_pointer_cast<RootShadowNode const>(
  101. rootNodeV1->ShadowNode::clone(
  102. ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(),
  103. std::make_shared<SharedShadowNodeList>(
  104. SharedShadowNodeList{shadowNodeV2})}));
  105. auto rootNodeV3 = std::static_pointer_cast<RootShadowNode const>(
  106. rootNodeV2->ShadowNode::clone(
  107. ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(),
  108. std::make_shared<SharedShadowNodeList>(
  109. SharedShadowNodeList{shadowNodeV3})}));
  110. auto rootNodeV4 = std::static_pointer_cast<RootShadowNode const>(
  111. rootNodeV3->ShadowNode::clone(
  112. ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(),
  113. std::make_shared<SharedShadowNodeList>(
  114. SharedShadowNodeList{shadowNodeV4})}));
  115. auto rootNodeV5 = std::static_pointer_cast<RootShadowNode const>(
  116. rootNodeV4->ShadowNode::clone(
  117. ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(),
  118. std::make_shared<SharedShadowNodeList>(
  119. SharedShadowNodeList{shadowNodeV5})}));
  120. auto rootNodeV6 = std::static_pointer_cast<RootShadowNode const>(
  121. rootNodeV5->ShadowNode::clone(
  122. ShadowNodeFragment{ShadowNodeFragment::propsPlaceholder(),
  123. std::make_shared<SharedShadowNodeList>(
  124. SharedShadowNodeList{shadowNodeV6})}));
  125. // Layout and diff
  126. std::vector<LayoutableShadowNode const *> affectedLayoutableNodesV1{};
  127. affectedLayoutableNodesV1.reserve(1024);
  128. std::const_pointer_cast<RootShadowNode>(rootNodeV1)
  129. ->layoutIfNeeded(&affectedLayoutableNodesV1);
  130. rootNodeV1->sealRecursive();
  131. std::vector<LayoutableShadowNode const *> affectedLayoutableNodesV2{};
  132. affectedLayoutableNodesV2.reserve(1024);
  133. std::const_pointer_cast<RootShadowNode>(rootNodeV2)
  134. ->layoutIfNeeded(&affectedLayoutableNodesV2);
  135. rootNodeV2->sealRecursive();
  136. std::vector<LayoutableShadowNode const *> affectedLayoutableNodesV3{};
  137. affectedLayoutableNodesV3.reserve(1024);
  138. std::const_pointer_cast<RootShadowNode>(rootNodeV3)
  139. ->layoutIfNeeded(&affectedLayoutableNodesV3);
  140. rootNodeV3->sealRecursive();
  141. std::vector<LayoutableShadowNode const *> affectedLayoutableNodesV4{};
  142. affectedLayoutableNodesV4.reserve(1024);
  143. std::const_pointer_cast<RootShadowNode>(rootNodeV4)
  144. ->layoutIfNeeded(&affectedLayoutableNodesV4);
  145. rootNodeV4->sealRecursive();
  146. std::vector<LayoutableShadowNode const *> affectedLayoutableNodesV5{};
  147. affectedLayoutableNodesV5.reserve(1024);
  148. std::const_pointer_cast<RootShadowNode>(rootNodeV5)
  149. ->layoutIfNeeded(&affectedLayoutableNodesV5);
  150. rootNodeV5->sealRecursive();
  151. std::vector<LayoutableShadowNode const *> affectedLayoutableNodesV6{};
  152. affectedLayoutableNodesV6.reserve(1024);
  153. std::const_pointer_cast<RootShadowNode>(rootNodeV6)
  154. ->layoutIfNeeded(&affectedLayoutableNodesV6);
  155. rootNodeV6->sealRecursive();
  156. // This block displays all the mutations for debugging purposes.
  157. /*
  158. LOG(ERROR) << "Num mutations: " << mutations.size();
  159. for (auto const &mutation : mutations) {
  160. switch (mutation.type) {
  161. case ShadowViewMutation::Create: {
  162. LOG(ERROR) << "CREATE " << mutation.newChildShadowView.tag;
  163. break;
  164. }
  165. case ShadowViewMutation::Delete: {
  166. LOG(ERROR) << "DELETE " << mutation.oldChildShadowView.tag;
  167. break;
  168. }
  169. case ShadowViewMutation::Remove: {
  170. LOG(ERROR) << "REMOVE " << mutation.oldChildShadowView.tag << " " <<
  171. mutation.index; break;
  172. }
  173. case ShadowViewMutation::Insert: {
  174. LOG(ERROR) << "INSERT " << mutation.newChildShadowView.tag << " " <<
  175. mutation.index; break;
  176. }
  177. case ShadowViewMutation::Update: {
  178. LOG(ERROR) << "UPDATE " << mutation.newChildShadowView.tag;
  179. break;
  180. }
  181. }
  182. }*/
  183. // Calculating mutations.
  184. auto mutations1 = calculateShadowViewMutations(
  185. DifferentiatorMode::OptimizedMoves, *rootNodeV1, *rootNodeV2);
  186. // The order and exact mutation instructions here may change at any time.
  187. // This test just ensures that any changes are intentional.
  188. // This test, in particular, ensures that inserting a node at the beginning
  189. // produces a single "Insert" instruction, and no remove/insert (move)
  190. // operations. All these nodes are laid out with absolute positioning, so
  191. // moving them around does not change layout.
  192. assert(mutations1.size() == 2);
  193. assert(mutations1[0].type == ShadowViewMutation::Create);
  194. assert(mutations1[0].newChildShadowView.tag == 100);
  195. assert(mutations1[1].type == ShadowViewMutation::Insert);
  196. assert(mutations1[1].newChildShadowView.tag == 100);
  197. assert(mutations1[1].index == 0);
  198. // Calculating mutations.
  199. auto mutations2 = calculateShadowViewMutations(
  200. DifferentiatorMode::OptimizedMoves, *rootNodeV2, *rootNodeV3);
  201. // The order and exact mutation instructions here may change at any time.
  202. // This test just ensures that any changes are intentional.
  203. // This test, in particular, ensures that removing a node at the beginning
  204. // produces a single remove (and delete) instruction, and no remove/insert
  205. // (move) operations. All these nodes are laid out with absolute positioning,
  206. // so moving them around does not change layout.
  207. assert(mutations2.size() == 2);
  208. assert(mutations2[0].type == ShadowViewMutation::Remove);
  209. assert(mutations2[0].oldChildShadowView.tag == 100);
  210. assert(mutations2[0].index == 0);
  211. assert(mutations2[1].type == ShadowViewMutation::Delete);
  212. assert(mutations2[1].oldChildShadowView.tag == 100);
  213. // Calculating mutations.
  214. auto mutations3 = calculateShadowViewMutations(
  215. DifferentiatorMode::OptimizedMoves, *rootNodeV3, *rootNodeV4);
  216. // The order and exact mutation instructions here may change at any time.
  217. // This test just ensures that any changes are intentional.
  218. // This test, in particular, ensures that removing a node in the middle
  219. // produces a single remove (and delete) instruction, and no remove/insert
  220. // (move) operations; and that simultaneously, we can insert a node at the
  221. // end. NOTE: This list of mutations has some unexpected "Update"
  222. // instructions, due to layout issues (some LayoutMetrics are 0). Not sure
  223. // why, but the point of this test is to make sure there aren't unnecessary
  224. // insert/deletes, so we can ignore for now.
  225. assert(mutations3.size() == 7);
  226. assert(mutations3[0].type == ShadowViewMutation::Update);
  227. assert(mutations3[1].type == ShadowViewMutation::Update);
  228. assert(mutations3[2].type == ShadowViewMutation::Update);
  229. assert(mutations3[3].type == ShadowViewMutation::Remove);
  230. assert(mutations3[3].oldChildShadowView.tag == 102);
  231. assert(mutations3[3].index == 1);
  232. assert(mutations3[4].type == ShadowViewMutation::Delete);
  233. assert(mutations3[4].oldChildShadowView.tag == 102);
  234. assert(mutations3[5].type == ShadowViewMutation::Create);
  235. assert(mutations3[5].newChildShadowView.tag == 104);
  236. assert(mutations3[6].type == ShadowViewMutation::Insert);
  237. assert(mutations3[6].newChildShadowView.tag == 104);
  238. assert(mutations3[6].index == 2);
  239. // Calculating mutations.
  240. auto mutations4 = calculateShadowViewMutations(
  241. DifferentiatorMode::OptimizedMoves, *rootNodeV4, *rootNodeV5);
  242. // The order and exact mutation instructions here may change at any time.
  243. // This test just ensures that any changes are intentional.
  244. // This test, in particular, ensures that inserting a child at the middle, and
  245. // at the end, and removing a node in the middle, produces the minimal set of
  246. // instructions. All these nodes are laid out with absolute positioning, so
  247. // moving them around does not change layout. NOTE: This list of mutations has
  248. // some unexpected "Update" instructions, due to layout issues (some
  249. // LayoutMetrics are 0). Not sure why, but the point of this test is to make
  250. // sure there aren't unnecessary insert/deletes, so we can ignore for now.
  251. assert(mutations4.size() == 9);
  252. assert(mutations4[0].type == ShadowViewMutation::Update);
  253. assert(mutations4[1].type == ShadowViewMutation::Update);
  254. assert(mutations4[2].type == ShadowViewMutation::Update);
  255. assert(mutations4[3].type == ShadowViewMutation::Remove);
  256. assert(mutations4[3].oldChildShadowView.tag == 103);
  257. assert(mutations4[3].index == 1);
  258. assert(mutations4[4].type == ShadowViewMutation::Delete);
  259. assert(mutations4[4].oldChildShadowView.tag == 103);
  260. assert(mutations4[5].type == ShadowViewMutation::Create);
  261. assert(mutations4[5].newChildShadowView.tag == 100);
  262. assert(mutations4[6].type == ShadowViewMutation::Create);
  263. assert(mutations4[6].newChildShadowView.tag == 102);
  264. assert(mutations4[7].type == ShadowViewMutation::Insert);
  265. assert(mutations4[7].newChildShadowView.tag == 100);
  266. assert(mutations4[7].index == 1);
  267. assert(mutations4[8].type == ShadowViewMutation::Insert);
  268. assert(mutations4[8].newChildShadowView.tag == 102);
  269. assert(mutations4[8].index == 3);
  270. auto mutations5 = calculateShadowViewMutations(
  271. DifferentiatorMode::OptimizedMoves, *rootNodeV5, *rootNodeV6);
  272. // The order and exact mutation instructions here may change at any time.
  273. // This test just ensures that any changes are intentional.
  274. // This test, in particular, ensures that inserting TWO children in the middle
  275. // produces the minimal set of instructions. All these nodes are laid out with
  276. // absolute positioning, so moving them around does not change layout.
  277. assert(mutations5.size() == 4);
  278. assert(mutations5[0].type == ShadowViewMutation::Create);
  279. assert(mutations5[0].newChildShadowView.tag == 103);
  280. assert(mutations5[1].type == ShadowViewMutation::Create);
  281. assert(mutations5[1].newChildShadowView.tag == 105);
  282. assert(mutations5[2].type == ShadowViewMutation::Insert);
  283. assert(mutations5[2].newChildShadowView.tag == 103);
  284. assert(mutations5[2].index == 2);
  285. assert(mutations5[3].type == ShadowViewMutation::Insert);
  286. assert(mutations5[3].newChildShadowView.tag == 105);
  287. assert(mutations5[3].index == 3);
  288. }
  289. } // namespace react
  290. } // namespace facebook