shadowTreeGeneration.h 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  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. #pragma once
  8. #include <glog/logging.h>
  9. #include <algorithm>
  10. #include <iostream>
  11. #include <memory>
  12. #include <random>
  13. #include <react/mounting/Differentiator.h>
  14. #include <react/mounting/stubs.h>
  15. #include <react/components/root/RootComponentDescriptor.h>
  16. #include <react/components/view/ViewComponentDescriptor.h>
  17. #include "Entropy.h"
  18. namespace facebook {
  19. namespace react {
  20. static Tag generateReactTag() {
  21. static Tag tag = 1000;
  22. return tag++;
  23. }
  24. class ShadowTreeEdge final {
  25. public:
  26. ShadowNode::Shared shadowNode{nullptr};
  27. ShadowNode::Shared parentShadowNode{nullptr};
  28. int index{0};
  29. };
  30. static bool traverseShadowTree(
  31. ShadowNode::Shared const &parentShadowNode,
  32. std::function<void(ShadowTreeEdge const &edge, bool &stop)> const
  33. &callback) {
  34. auto index = int{0};
  35. for (auto const &childNode : parentShadowNode->getChildren()) {
  36. auto stop = bool{false};
  37. callback(ShadowTreeEdge{childNode, parentShadowNode, index}, stop);
  38. if (stop) {
  39. return true;
  40. }
  41. if (traverseShadowTree(childNode, callback)) {
  42. return true;
  43. }
  44. index++;
  45. }
  46. return false;
  47. }
  48. static int countShadowNodes(ShadowNode::Shared const &rootShadowNode) {
  49. auto counter = int{0};
  50. traverseShadowTree(
  51. rootShadowNode,
  52. [&](ShadowTreeEdge const &edge, bool &stop) { counter++; });
  53. return counter;
  54. }
  55. static ShadowTreeEdge findShadowNodeWithIndex(
  56. ShadowNode::Shared const &rootNode,
  57. int index) {
  58. auto counter = int{0};
  59. auto result = ShadowTreeEdge{};
  60. traverseShadowTree(rootNode, [&](ShadowTreeEdge const &edge, bool &stop) {
  61. if (index == counter) {
  62. result = edge;
  63. }
  64. counter++;
  65. });
  66. return result;
  67. }
  68. static ShadowTreeEdge findRandomShadowNode(
  69. Entropy const &entropy,
  70. ShadowNode::Shared const &rootShadowNode) {
  71. auto count = countShadowNodes(rootShadowNode);
  72. return findShadowNodeWithIndex(
  73. rootShadowNode,
  74. entropy.random<int>(1 /* Excluding a root node */, count - 1));
  75. }
  76. static ShadowNode::ListOfShared cloneSharedShadowNodeList(
  77. ShadowNode::ListOfShared const &list) {
  78. auto result = ShadowNode::ListOfShared{};
  79. result.reserve(list.size());
  80. for (auto const &shadowNode : list) {
  81. result.push_back(shadowNode->clone({}));
  82. }
  83. return result;
  84. }
  85. static inline ShadowNode::Unshared messWithChildren(
  86. Entropy const &entropy,
  87. ShadowNode const &shadowNode) {
  88. auto children = shadowNode.getChildren();
  89. children = cloneSharedShadowNodeList(children);
  90. entropy.shuffle(children);
  91. return shadowNode.clone(
  92. {ShadowNodeFragment::propsPlaceholder(),
  93. std::make_shared<ShadowNode::ListOfShared const>(children)});
  94. }
  95. static inline ShadowNode::Unshared messWithLayotableOnlyFlag(
  96. Entropy const &entropy,
  97. ShadowNode const &shadowNode) {
  98. auto oldProps = shadowNode.getProps();
  99. auto newProps = shadowNode.getComponentDescriptor().cloneProps(
  100. oldProps, RawProps(folly::dynamic::object()));
  101. auto &viewProps =
  102. const_cast<ViewProps &>(static_cast<ViewProps const &>(*newProps));
  103. if (entropy.random<bool>(0.1)) {
  104. viewProps.nativeId = entropy.random<bool>() ? "42" : "";
  105. }
  106. if (entropy.random<bool>(0.1)) {
  107. viewProps.backgroundColor =
  108. entropy.random<bool>() ? SharedColor() : whiteColor();
  109. }
  110. if (entropy.random<bool>(0.1)) {
  111. viewProps.foregroundColor =
  112. entropy.random<bool>() ? SharedColor() : blackColor();
  113. }
  114. if (entropy.random<bool>(0.1)) {
  115. viewProps.shadowColor =
  116. entropy.random<bool>() ? SharedColor() : blackColor();
  117. }
  118. if (entropy.random<bool>(0.1)) {
  119. viewProps.accessible = entropy.random<bool>();
  120. }
  121. if (entropy.random<bool>(0.1)) {
  122. viewProps.zIndex = entropy.random<bool>() ? 1 : 0;
  123. }
  124. if (entropy.random<bool>(0.1)) {
  125. viewProps.pointerEvents = entropy.random<bool>() ? PointerEventsMode::Auto
  126. : PointerEventsMode::None;
  127. }
  128. if (entropy.random<bool>(0.1)) {
  129. viewProps.transform = entropy.random<bool>() ? Transform::Identity()
  130. : Transform::Perspective(42);
  131. }
  132. return shadowNode.clone({newProps});
  133. }
  134. static inline ShadowNode::Unshared messWithYogaStyles(
  135. Entropy const &entropy,
  136. ShadowNode const &shadowNode) {
  137. folly::dynamic dynamic = folly::dynamic::object();
  138. if (entropy.random<bool>()) {
  139. dynamic["flexDirection"] = entropy.random<bool>() ? "row" : "column";
  140. }
  141. std::vector<std::string> properties = {
  142. "flex", "flexGrow", "flexShrink", "flexBasis",
  143. "left", "top", "marginLeft", "marginTop",
  144. "marginRight", "marginBottom", "paddingLeft", "paddingTop",
  145. "paddingRight", "paddingBottom", "width", "height",
  146. "maxWidth", "maxHeight", "minWidth", "minHeight",
  147. };
  148. for (auto const &property : properties) {
  149. if (entropy.random<bool>(0.1)) {
  150. dynamic[property] = entropy.random<int>(0, 1024);
  151. }
  152. }
  153. auto oldProps = shadowNode.getProps();
  154. auto newProps = shadowNode.getComponentDescriptor().cloneProps(
  155. oldProps, RawProps(dynamic));
  156. return shadowNode.clone({newProps});
  157. }
  158. using ShadowNodeAlteration = std::function<
  159. ShadowNode::Unshared(Entropy const &entropy, ShadowNode const &shadowNode)>;
  160. static inline void alterShadowTree(
  161. Entropy const &entropy,
  162. RootShadowNode::Shared &rootShadowNode,
  163. ShadowNodeAlteration alteration) {
  164. auto edge = findRandomShadowNode(entropy, rootShadowNode);
  165. rootShadowNode =
  166. std::static_pointer_cast<RootShadowNode>(rootShadowNode->cloneTree(
  167. edge.shadowNode->getFamily(), [&](ShadowNode const &oldShadowNode) {
  168. return alteration(entropy, oldShadowNode);
  169. }));
  170. }
  171. static inline void alterShadowTree(
  172. Entropy const &entropy,
  173. RootShadowNode::Shared &rootShadowNode,
  174. std::vector<ShadowNodeAlteration> alterations) {
  175. auto i = entropy.random<int>(0, alterations.size() - 1);
  176. alterShadowTree(entropy, rootShadowNode, alterations[i]);
  177. }
  178. static SharedViewProps generateDefaultProps(
  179. ComponentDescriptor const &componentDescriptor) {
  180. return std::static_pointer_cast<ViewProps const>(
  181. componentDescriptor.cloneProps(nullptr, RawProps{}));
  182. }
  183. static inline ShadowNode::Shared generateShadowNodeTree(
  184. Entropy const &entropy,
  185. ComponentDescriptor const &componentDescriptor,
  186. int size,
  187. int deviation = 3) {
  188. if (size <= 1) {
  189. auto family = componentDescriptor.createFamily(
  190. {generateReactTag(), SurfaceId(1), nullptr}, nullptr);
  191. return componentDescriptor.createShadowNode(
  192. ShadowNodeFragment{generateDefaultProps(componentDescriptor)}, family);
  193. }
  194. auto items = std::vector<int>(size);
  195. std::fill(items.begin(), items.end(), 1);
  196. auto chunks = entropy.distribute(items, deviation);
  197. auto children = ShadowNode::ListOfShared{};
  198. for (auto const &chunk : chunks) {
  199. children.push_back(
  200. generateShadowNodeTree(entropy, componentDescriptor, chunk.size()));
  201. }
  202. auto family = componentDescriptor.createFamily(
  203. {generateReactTag(), SurfaceId(1), nullptr}, nullptr);
  204. return componentDescriptor.createShadowNode(
  205. ShadowNodeFragment{generateDefaultProps(componentDescriptor),
  206. std::make_shared<SharedShadowNodeList>(children)},
  207. family);
  208. }
  209. } // namespace react
  210. } // namespace facebook