@@ -87,7 +87,7 @@ static void testShadowNodeTreeLifeCycle(
8787 {
8888 &messWithChildren,
8989 &messWithYogaStyles,
90- &messWithLayotableOnlyFlag ,
90+ &messWithLayoutableOnlyFlag ,
9191 });
9292
9393 std::vector<LayoutableShadowNode const *> affectedLayoutableNodes{};
@@ -142,7 +142,155 @@ static void testShadowNodeTreeLifeCycle(
142142
143143 // There are some issues getting `getDebugDescription` to compile
144144 // under test on Android for now.
145- #ifndef ANDROID
145+ #ifdef RN_DEBUG_STRING_CONVERTIBLE
146+ LOG (ERROR) << " Shadow Tree before: \n "
147+ << currentRootNode->getDebugDescription ();
148+ LOG (ERROR) << " Shadow Tree after: \n "
149+ << nextRootNode->getDebugDescription ();
150+
151+ LOG (ERROR) << " View Tree before: \n "
152+ << getDebugDescription (viewTree.getRootStubView (), {});
153+ LOG (ERROR) << " View Tree after: \n "
154+ << getDebugDescription (
155+ rebuiltViewTree.getRootStubView (), {});
156+
157+ LOG (ERROR) << " Mutations:"
158+ << " \n "
159+ << getDebugDescription (mutations, {});
160+ #endif
161+
162+ FAIL ();
163+ }
164+
165+ currentRootNode = nextRootNode;
166+ }
167+ }
168+
169+ SUCCEED ();
170+ }
171+
172+ static void testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening (
173+ uint_fast32_t seed,
174+ int treeSize,
175+ int repeats,
176+ int stages) {
177+ auto entropy = seed == 0 ? Entropy () : Entropy (seed);
178+
179+ auto eventDispatcher = EventDispatcher::Shared{};
180+ auto contextContainer = std::make_shared<ContextContainer>();
181+ auto componentDescriptorParameters =
182+ ComponentDescriptorParameters{eventDispatcher, contextContainer, nullptr };
183+ auto viewComponentDescriptor =
184+ ViewComponentDescriptor (componentDescriptorParameters);
185+ auto rootComponentDescriptor =
186+ RootComponentDescriptor (componentDescriptorParameters);
187+ auto noopEventEmitter =
188+ std::make_shared<ViewEventEmitter const >(nullptr , -1 , eventDispatcher);
189+
190+ auto allNodes = std::vector<ShadowNode::Shared>{};
191+
192+ for (int i = 0 ; i < repeats; i++) {
193+ allNodes.clear ();
194+
195+ auto family = rootComponentDescriptor.createFamily (
196+ {Tag (1 ), SurfaceId (1 ), nullptr }, nullptr );
197+
198+ // Creating an initial root shadow node.
199+ auto emptyRootNode = std::const_pointer_cast<RootShadowNode>(
200+ std::static_pointer_cast<RootShadowNode const >(
201+ rootComponentDescriptor.createShadowNode (
202+ ShadowNodeFragment{RootShadowNode::defaultSharedProps ()},
203+ family)));
204+
205+ // Applying size constraints.
206+ emptyRootNode = emptyRootNode->clone (
207+ LayoutConstraints{
208+ Size{512 , 0 }, Size{512 , std::numeric_limits<Float>::infinity ()}},
209+ LayoutContext{});
210+
211+ // Generation of a random tree.
212+ auto singleRootChildNode =
213+ generateShadowNodeTree (entropy, viewComponentDescriptor, treeSize);
214+
215+ // Injecting a tree into the root node.
216+ auto currentRootNode = std::static_pointer_cast<RootShadowNode const >(
217+ emptyRootNode->ShadowNode ::clone (ShadowNodeFragment{
218+ ShadowNodeFragment::propsPlaceholder (),
219+ std::make_shared<SharedShadowNodeList>(
220+ SharedShadowNodeList{singleRootChildNode})}));
221+
222+ // Building an initial view hierarchy.
223+ auto viewTree = buildStubViewTreeWithoutUsingDifferentiator (*emptyRootNode);
224+ viewTree.mutate (
225+ calculateShadowViewMutations (*emptyRootNode, *currentRootNode, true ));
226+
227+ for (int j = 0 ; j < stages; j++) {
228+ auto nextRootNode = currentRootNode;
229+
230+ // Mutating the tree.
231+ alterShadowTree (
232+ entropy,
233+ nextRootNode,
234+ {
235+ &messWithYogaStyles,
236+ &messWithLayoutableOnlyFlag,
237+ });
238+ alterShadowTree (entropy, nextRootNode, &messWithNodeFlattenednessFlags);
239+ alterShadowTree (entropy, nextRootNode, &messWithChildren);
240+
241+ std::vector<LayoutableShadowNode const *> affectedLayoutableNodes{};
242+ affectedLayoutableNodes.reserve (1024 );
243+
244+ // Laying out the tree.
245+ std::const_pointer_cast<RootShadowNode>(nextRootNode)
246+ ->layoutIfNeeded (&affectedLayoutableNodes);
247+
248+ nextRootNode->sealRecursive ();
249+ allNodes.push_back (nextRootNode);
250+
251+ // Calculating mutations.
252+ auto mutations =
253+ calculateShadowViewMutations (*currentRootNode, *nextRootNode, true );
254+
255+ // Make sure that in a single frame, a DELETE for a
256+ // view is not followed by a CREATE for the same view.
257+ {
258+ std::vector<int > deletedTags{};
259+ for (auto const &mutation : mutations) {
260+ if (mutation.type == ShadowViewMutation::Type::Delete) {
261+ deletedTags.push_back (mutation.oldChildShadowView .tag );
262+ }
263+ }
264+ for (auto const &mutation : mutations) {
265+ if (mutation.type == ShadowViewMutation::Type::Create) {
266+ if (std::find (
267+ deletedTags.begin (),
268+ deletedTags.end (),
269+ mutation.newChildShadowView .tag ) != deletedTags.end ()) {
270+ LOG (ERROR) << " Deleted tag was recreated in mutations list: ["
271+ << mutation.newChildShadowView .tag << " ]" ;
272+ FAIL ();
273+ }
274+ }
275+ }
276+ }
277+
278+ // Mutating the view tree.
279+ viewTree.mutate (mutations);
280+
281+ // Building a view tree to compare with.
282+ auto rebuiltViewTree =
283+ buildStubViewTreeWithoutUsingDifferentiator (*nextRootNode);
284+
285+ // Comparing the newly built tree with the updated one.
286+ if (rebuiltViewTree != viewTree) {
287+ // Something went wrong.
288+
289+ LOG (ERROR) << " Entropy seed: " << entropy.getSeed () << " \n " ;
290+
291+ // There are some issues getting `getDebugDescription` to compile
292+ // under test on Android for now.
293+ #ifdef RN_DEBUG_STRING_CONVERTIBLE
146294 LOG (ERROR) << " Shadow Tree before: \n "
147295 << currentRootNode->getDebugDescription ();
148296 LOG (ERROR) << " Shadow Tree after: \n "
@@ -203,3 +351,33 @@ TEST(
203351 /* repeats */ 512 ,
204352 /* stages */ 32 );
205353}
354+
355+ TEST (
356+ ShadowTreeLifecyleTest,
357+ unstableSmallerTreeFewerIterationsExtensiveFlatteningUnflattening) {
358+ testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening (
359+ /* seed */ 1337 ,
360+ /* size */ 32 ,
361+ /* repeats */ 32 ,
362+ /* stages */ 32 );
363+ }
364+
365+ TEST (
366+ ShadowTreeLifecyleTest,
367+ unstableBiggerTreeFewerIterationsExtensiveFlatteningUnflattening) {
368+ testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening (
369+ /* seed */ 1337 ,
370+ /* size */ 256 ,
371+ /* repeats */ 32 ,
372+ /* stages */ 32 );
373+ }
374+
375+ TEST (
376+ ShadowTreeLifecyleTest,
377+ unstableSmallerTreeMoreIterationsExtensiveFlatteningUnflattening) {
378+ testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening (
379+ /* seed */ 1337 ,
380+ /* size */ 32 ,
381+ /* repeats */ 512 ,
382+ /* stages */ 32 );
383+ }
0 commit comments