1 /*
2  * Copyright 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.compose.ui.node
18 
19 import androidx.compose.ui.Modifier
20 import androidx.compose.ui.graphics.drawscope.ContentDrawScope
21 import androidx.compose.ui.layout.Measurable
22 import androidx.compose.ui.layout.MeasureResult
23 import androidx.compose.ui.layout.MeasureScope
24 import androidx.compose.ui.semantics.SemanticsPropertyReceiver
25 import androidx.compose.ui.unit.Constraints
26 import com.google.common.truth.Truth.assertThat
27 import org.junit.Test
28 import org.junit.runner.RunWith
29 import org.junit.runners.JUnit4
30 
31 @RunWith(JUnit4::class)
32 class DelegatingNodeTest {
33 
34     @Test
testKindSetIncludesDelegatesnull35     fun testKindSetIncludesDelegates() {
36         assertThat(DelegatedWrapper { DrawMod() }.kindSet).isEqualTo(Nodes.Any or Nodes.Draw)
37     }
38 
39     @Test
testKindSetUpdatesAfternull40     fun testKindSetUpdatesAfter() {
41         val a = DrawMod("a")
42         val b = DelegatedWrapper { LayoutMod("b") }
43         val c = object : DelegatingNode() {}
44         val chain = layout(a, b, c)
45         assert(chain.has(Nodes.Draw))
46         assert(chain.has(Nodes.Layout))
47         assert(!chain.has(Nodes.Semantics))
48 
49         assert(a.kindSet == Nodes.Any or Nodes.Draw)
50         assert(a.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Layout)
51 
52         assert(b.kindSet == Nodes.Any or Nodes.Layout)
53         assert(b.aggregateChildKindSet == Nodes.Any or Nodes.Layout)
54 
55         assert(c.kindSet == Nodes.Any.mask)
56         assert(c.aggregateChildKindSet == Nodes.Any.mask)
57 
58         c.delegateUnprotected(SemanticsMod("c"))
59         assert(chain.has(Nodes.Semantics))
60 
61         assert(a.kindSet == Nodes.Any or Nodes.Draw)
62         assert(
63             a.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Layout or Nodes.Semantics
64         )
65 
66         assert(b.kindSet == Nodes.Any or Nodes.Layout)
67         assert(b.aggregateChildKindSet == Nodes.Any or Nodes.Layout or Nodes.Semantics)
68 
69         assert(c.kindSet == Nodes.Any or Nodes.Semantics)
70         assert(c.aggregateChildKindSet == Nodes.Any or Nodes.Semantics)
71     }
72 
73     @Test
testAsKindReturnsDelegatenull74     fun testAsKindReturnsDelegate() {
75         val node = DelegatedWrapper { DrawMod() }
76         assert(node.isKind(Nodes.Draw))
77         assert(node.asKind(Nodes.Draw) is DrawMod)
78         assert(node.asKind(Nodes.Draw) === node.wrapped)
79     }
80 
81     @Test
testNestedDelegatesHaveNodePointersCorrectlyUpdatednull82     fun testNestedDelegatesHaveNodePointersCorrectlyUpdated() {
83         val d = DrawMod()
84         val c = DrawMod()
85         val b =
86             object : DelegatingNode() {
87                 val c = delegate(c)
88             }
89         val a =
90             object : DelegatingNode() {
91                 val b = delegate(b)
92                 val d = delegate(d)
93             }
94 
95         assert(a.node === a)
96         assert(b.node === a)
97         assert(c.node === a)
98         assert(d.node === a)
99     }
100 
101     @Test
testAsKindReturnsNestedDelegatenull102     fun testAsKindReturnsNestedDelegate() {
103         val node = DelegatedWrapper { DelegatedWrapper { DrawMod() } }
104         assert(node.isKind(Nodes.Draw))
105         assert(node.asKind(Nodes.Draw) is DrawMod)
106         assert(node.asKind(Nodes.Draw) === node.wrapped.wrapped)
107     }
108 
109     @Test
testAsKindReturnsSelfnull110     fun testAsKindReturnsSelf() {
111         val node =
112             object : DrawModifierNode, DelegatingNode() {
113                 val wrapped = delegate(DrawMod())
114 
115                 override fun ContentDrawScope.draw() {
116                     with(wrapped) { draw() }
117                 }
118             }
119         assert(node.isKind(Nodes.Draw))
120         assert(node.asKind(Nodes.Draw) is DrawModifierNode)
121         assert(node.asKind(Nodes.Draw) === node)
122     }
123 
124     @Test
testAsKindMultipleDelegatesReturnsLastnull125     fun testAsKindMultipleDelegatesReturnsLast() {
126         val node =
127             object : DelegatingNode() {
128                 val first = delegate(DrawMod())
129                 val second = delegate(DrawMod())
130             }
131         assert(node.isKind(Nodes.Draw))
132         assert(node.asKind(Nodes.Draw) is DrawModifierNode)
133         assert(node.asKind(Nodes.Draw) === node.second)
134     }
135 
136     @Test
testDispatchForMultipleDelegatesSameKindnull137     fun testDispatchForMultipleDelegatesSameKind() {
138         val node =
139             object : DelegatingNode() {
140                 val first = delegate(DelegatedWrapper { DrawMod("first") })
141                 val second = delegate(DrawMod("second"))
142             }
143         assertDispatchOrder(node, Nodes.Draw, node.first.wrapped, node.second)
144     }
145 
146     @Test
testDispatchForSelfOnlyDispatchesToSelfnull147     fun testDispatchForSelfOnlyDispatchesToSelf() {
148         val node =
149             object : DrawModifierNode, DelegatingNode() {
150                 val wrapped = delegate(DrawMod())
151 
152                 override fun ContentDrawScope.draw() {
153                     with(wrapped) { draw() }
154                 }
155             }
156         assertDispatchOrder(node, Nodes.Draw, node)
157     }
158 
159     @Test
testDispatchNestedSelfStopsnull160     fun testDispatchNestedSelfStops() {
161         val node =
162             object : DelegatingNode() {
163                 val first = delegate(DrawMod())
164                 val second = delegate(DrawMod())
165                 val third =
166                     delegate(
167                         object : DrawModifierNode, DelegatingNode() {
168                             val first = delegate(DrawMod())
169                             val second = delegate(DrawMod())
170 
171                             override fun ContentDrawScope.draw() {
172                                 with(first) { draw() }
173                             }
174                         }
175                     )
176             }
177         assertDispatchOrder(node, Nodes.Draw, node.first, node.second, node.third)
178     }
179 
180     @Test
testHeadToTailNoDelegatesnull181     fun testHeadToTailNoDelegates() {
182         val a = DrawMod("a")
183         val b = DrawMod("b")
184         val chain = layout(a, b)
185         val recorder = Recorder()
186         chain.headToTail(Nodes.Draw, recorder)
187         assertThat(recorder.recorded).isEqualTo(listOf(a, b))
188     }
189 
190     @Test
testHeadToTailWithDelegatenull191     fun testHeadToTailWithDelegate() {
192         val a = DelegatedWrapper { DrawMod() }
193         val b = DrawMod()
194         val chain = layout(a, b)
195         val recorder = Recorder()
196         chain.headToTail(Nodes.Draw, recorder)
197         assertThat(recorder.recorded).isEqualTo(listOf(a.wrapped, b))
198     }
199 
200     @Test
testVisitSubtreeWithDelegatesnull201     fun testVisitSubtreeWithDelegates() {
202         val x = DrawMod("x")
203         val a = DelegatedWrapper { DrawMod("a") }
204         val b = DrawMod("b")
205         val c = DelegatedWrapper { DrawMod("c") }
206         val d = DrawMod("d")
207         layout(x, a, b) {
208             layout(c)
209             layout(d)
210         }
211         val recorder = Recorder()
212         x.visitSubtree(Nodes.Draw, block = recorder)
213         assertThat(recorder.recorded)
214             .isEqualTo(
215                 listOf(
216                     a.wrapped,
217                     b,
218                     c.wrapped,
219                     d,
220                 )
221             )
222     }
223 
224     @Test
testVisitAncestorsWithDelegatesnull225     fun testVisitAncestorsWithDelegates() {
226         val x = DrawMod("x")
227         val a = DelegatedWrapper { DrawMod("a") }
228         val b = DrawMod("b")
229         val c = DelegatedWrapper { DrawMod("c") }
230         val d = DrawMod("d")
231         layout(a) { layout(b) { layout(c) { layout(d, x) } } }
232         val recorder = Recorder()
233         x.visitAncestors(Nodes.Draw, block = recorder)
234         assertThat(recorder.recorded)
235             .isEqualTo(
236                 listOf(
237                     d,
238                     c.wrapped,
239                     b,
240                     a.wrapped,
241                 )
242             )
243     }
244 
245     @Test
testUndelegatenull246     fun testUndelegate() {
247         val node = object : DelegatingNode() {}
248         val chain = layout(node)
249 
250         assert(!node.isKind(Nodes.Draw))
251         assert(!chain.has(Nodes.Draw))
252 
253         val draw = node.delegateUnprotected(DrawMod())
254 
255         assert(node.isKind(Nodes.Draw))
256         assert(chain.has(Nodes.Draw))
257         assert(node.asKind(Nodes.Draw) === draw)
258 
259         node.undelegateUnprotected(draw)
260 
261         assert(!draw.isAttached)
262         assert(node.isAttached)
263 
264         assert(!node.isKind(Nodes.Draw))
265         assert(!chain.has(Nodes.Draw))
266     }
267 
268     @Test
testUndelegateWithMultipleDelegatesnull269     fun testUndelegateWithMultipleDelegates() {
270         val node = object : DelegatingNode() {}
271         val chain = layout(node)
272 
273         assert(!node.isKind(Nodes.Draw))
274         assert(!chain.has(Nodes.Draw))
275 
276         val draw = node.delegateUnprotected(DrawMod())
277         val layout = node.delegateUnprotected(LayoutMod())
278         val semantics = node.delegateUnprotected(SemanticsMod())
279         val draw2 = node.delegateUnprotected(DrawMod())
280 
281         assert(node.isKind(Nodes.Semantics))
282         assert(chain.has(Nodes.Semantics))
283         assert(node.asKind(Nodes.Semantics) === semantics)
284 
285         assert(node.isKind(Nodes.Draw))
286         assert(chain.has(Nodes.Draw))
287         assert(node.asKind(Nodes.Draw) === draw2)
288 
289         assert(node.isKind(Nodes.Layout))
290         assert(chain.has(Nodes.Layout))
291         assert(node.asKind(Nodes.Layout) === layout)
292 
293         node.undelegateUnprotected(semantics)
294 
295         assert(!node.isKind(Nodes.Semantics))
296         assert(!chain.has(Nodes.Semantics))
297 
298         assert(node.isKind(Nodes.Draw))
299         assert(chain.has(Nodes.Draw))
300         assert(node.asKind(Nodes.Draw) === draw2)
301 
302         assert(node.isKind(Nodes.Layout))
303         assert(chain.has(Nodes.Layout))
304         assert(node.asKind(Nodes.Layout) === layout)
305 
306         node.undelegateUnprotected(draw2)
307 
308         assert(node.isKind(Nodes.Draw))
309         assert(chain.has(Nodes.Draw))
310         assert(node.asKind(Nodes.Draw) === draw)
311 
312         assert(node.isKind(Nodes.Layout))
313         assert(chain.has(Nodes.Layout))
314         assert(node.asKind(Nodes.Layout) === layout)
315     }
316 
317     @Test
testDelegateUndelegateInChainnull318     fun testDelegateUndelegateInChain() {
319         val a = object : DelegatingNode() {}
320         val b = object : DelegatingNode() {}
321         val c = object : DelegatingNode() {}
322         val chain = layout(a, b, c)
323         assert(!chain.has(Nodes.Draw))
324         assert(!chain.has(Nodes.Semantics))
325         assert(!chain.has(Nodes.Layout))
326 
327         val draw = c.delegateUnprotected(DrawMod())
328         assert(chain.has(Nodes.Draw))
329         assert(!chain.has(Nodes.Semantics))
330         assert(!chain.has(Nodes.Layout))
331         assert(a.kindSet == Nodes.Any.mask)
332         assert(b.kindSet == Nodes.Any.mask)
333         assert(c.kindSet == Nodes.Any or Nodes.Draw)
334         assert(a.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
335         assert(b.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
336         assert(c.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
337 
338         val sem = b.delegateUnprotected(SemanticsMod())
339         assert(chain.has(Nodes.Draw))
340         assert(chain.has(Nodes.Semantics))
341         assert(!chain.has(Nodes.Layout))
342         assert(a.kindSet == Nodes.Any.mask)
343         assert(b.kindSet == Nodes.Any or Nodes.Semantics)
344         assert(c.kindSet == Nodes.Any or Nodes.Draw)
345         assert(a.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
346         assert(b.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
347         assert(c.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
348 
349         val lm = a.delegateUnprotected(LayoutMod())
350         assert(chain.has(Nodes.Draw))
351         assert(chain.has(Nodes.Semantics))
352         assert(chain.has(Nodes.Layout))
353         assert(a.kindSet == Nodes.Any or Nodes.Layout)
354         assert(b.kindSet == Nodes.Any or Nodes.Semantics)
355         assert(c.kindSet == Nodes.Any or Nodes.Draw)
356         assert(
357             a.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics or Nodes.Layout
358         )
359         assert(b.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
360         assert(c.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
361 
362         c.undelegateUnprotected(draw)
363         assert(!chain.has(Nodes.Draw))
364         assert(chain.has(Nodes.Semantics))
365         assert(chain.has(Nodes.Layout))
366         assert(a.kindSet == Nodes.Any or Nodes.Layout)
367         assert(b.kindSet == Nodes.Any or Nodes.Semantics)
368         assert(c.kindSet == Nodes.Any.mask)
369         assert(a.aggregateChildKindSet == Nodes.Any or Nodes.Semantics or Nodes.Layout)
370         assert(b.aggregateChildKindSet == Nodes.Any or Nodes.Semantics)
371         assert(c.aggregateChildKindSet == Nodes.Any.mask)
372 
373         b.undelegateUnprotected(sem)
374         assert(!chain.has(Nodes.Draw))
375         assert(!chain.has(Nodes.Semantics))
376         assert(chain.has(Nodes.Layout))
377         assert(a.kindSet == Nodes.Any or Nodes.Layout)
378         assert(b.kindSet == Nodes.Any.mask)
379         assert(c.kindSet == Nodes.Any.mask)
380         assert(a.aggregateChildKindSet == Nodes.Any.mask or Nodes.Layout)
381         assert(b.aggregateChildKindSet == Nodes.Any.mask)
382         assert(c.aggregateChildKindSet == Nodes.Any.mask)
383 
384         a.undelegateUnprotected(lm)
385         assert(!chain.has(Nodes.Draw))
386         assert(!chain.has(Nodes.Semantics))
387         assert(!chain.has(Nodes.Layout))
388         assert(a.kindSet == Nodes.Any.mask)
389         assert(b.kindSet == Nodes.Any.mask)
390         assert(c.kindSet == Nodes.Any.mask)
391         assert(a.aggregateChildKindSet == Nodes.Any.mask)
392         assert(b.aggregateChildKindSet == Nodes.Any.mask)
393         assert(c.aggregateChildKindSet == Nodes.Any.mask)
394     }
395 
396     @Test
testDelegateUndelegateInNodenull397     fun testDelegateUndelegateInNode() {
398         val node = object : DelegatingNode() {}
399         val chain = layout(node)
400         assert(!chain.has(Nodes.Draw))
401         assert(!chain.has(Nodes.Semantics))
402         assert(!chain.has(Nodes.Layout))
403 
404         val draw = node.delegateUnprotected(DrawMod())
405         assert(chain.has(Nodes.Draw))
406         assert(!chain.has(Nodes.Semantics))
407         assert(!chain.has(Nodes.Layout))
408         assert(node.kindSet == Nodes.Any or Nodes.Draw)
409         assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
410 
411         val sem = node.delegateUnprotected(SemanticsMod())
412         assert(chain.has(Nodes.Draw))
413         assert(chain.has(Nodes.Semantics))
414         assert(!chain.has(Nodes.Layout))
415         assert(node.kindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
416         assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
417 
418         val lm = node.delegateUnprotected(LayoutMod())
419         assert(chain.has(Nodes.Draw))
420         assert(chain.has(Nodes.Semantics))
421         assert(chain.has(Nodes.Layout))
422         assert(node.kindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics or Nodes.Layout)
423         assert(
424             node.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics or Nodes.Layout
425         )
426 
427         node.undelegateUnprotected(draw)
428         assert(!chain.has(Nodes.Draw))
429         assert(chain.has(Nodes.Semantics))
430         assert(chain.has(Nodes.Layout))
431         assert(node.kindSet == Nodes.Any or Nodes.Semantics or Nodes.Layout)
432         assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Semantics or Nodes.Layout)
433 
434         node.undelegateUnprotected(sem)
435         assert(!chain.has(Nodes.Draw))
436         assert(!chain.has(Nodes.Semantics))
437         assert(chain.has(Nodes.Layout))
438         assert(node.kindSet == Nodes.Any or Nodes.Layout)
439         assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Layout)
440 
441         node.undelegateUnprotected(lm)
442         assert(!chain.has(Nodes.Draw))
443         assert(!chain.has(Nodes.Semantics))
444         assert(!chain.has(Nodes.Layout))
445         assert(node.kindSet == Nodes.Any.mask)
446         assert(node.aggregateChildKindSet == Nodes.Any.mask)
447     }
448 
449     @Test
testDelegateUndelegateNestednull450     fun testDelegateUndelegateNested() {
451         val node = object : DelegatingNode() {}
452         val chain = layout(node)
453         assert(!chain.has(Nodes.Draw))
454         assert(!chain.has(Nodes.Semantics))
455         assert(!chain.has(Nodes.Layout))
456 
457         val draw = node.delegateUnprotected(DelegatedWrapper { DrawMod() })
458         assert(chain.has(Nodes.Draw))
459         assert(!chain.has(Nodes.Semantics))
460         assert(!chain.has(Nodes.Layout))
461         assert(node.kindSet == Nodes.Any or Nodes.Draw)
462         assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
463 
464         val sem = draw.delegateUnprotected(DelegatedWrapper { SemanticsMod() })
465         assert(chain.has(Nodes.Draw))
466         assert(chain.has(Nodes.Semantics))
467         assert(!chain.has(Nodes.Layout))
468         assert(node.kindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
469         assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
470 
471         val lm = sem.delegateUnprotected(LayoutMod())
472         assert(chain.has(Nodes.Draw))
473         assert(chain.has(Nodes.Semantics))
474         assert(chain.has(Nodes.Layout))
475         assert(node.kindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics or Nodes.Layout)
476         assert(
477             node.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics or Nodes.Layout
478         )
479 
480         sem.undelegateUnprotected(lm)
481         assert(chain.has(Nodes.Draw))
482         assert(chain.has(Nodes.Semantics))
483         assert(!chain.has(Nodes.Layout))
484         assert(node.kindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
485         assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
486 
487         draw.undelegateUnprotected(sem)
488         assert(chain.has(Nodes.Draw))
489         assert(!chain.has(Nodes.Semantics))
490         assert(!chain.has(Nodes.Layout))
491         assert(node.kindSet == Nodes.Any or Nodes.Draw)
492         assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
493 
494         node.undelegateUnprotected(draw)
495         assert(!chain.has(Nodes.Draw))
496         assert(!chain.has(Nodes.Semantics))
497         assert(!chain.has(Nodes.Layout))
498         assert(node.kindSet == Nodes.Any.mask)
499         assert(node.aggregateChildKindSet == Nodes.Any.mask)
500     }
501 
502     @Test
testUndelegateForNestedDelegatenull503     fun testUndelegateForNestedDelegate() {
504         val a = object : DelegatingNode() {}
505         val chain = layout(a)
506 
507         val b = a.delegateUnprotected(object : DelegatingNode() {})
508         val c = a.delegateUnprotected(object : DelegatingNode() {})
509 
510         assert(!chain.has(Nodes.Draw))
511         assert(!a.isKind(Nodes.Draw))
512 
513         val draw = c.delegateUnprotected(DrawMod())
514 
515         assert(chain.has(Nodes.Draw))
516         assert(a.isKind(Nodes.Draw))
517         assert(!b.isKind(Nodes.Draw))
518         assert(c.isKind(Nodes.Draw))
519 
520         c.undelegateUnprotected(draw)
521 
522         assert(!chain.has(Nodes.Draw))
523         assert(!a.isKind(Nodes.Draw))
524         assert(!b.isKind(Nodes.Draw))
525         assert(!c.isKind(Nodes.Draw))
526     }
527 
528     @Test
testInvalidateInsertedNodenull529     fun testInvalidateInsertedNode() {
530         val node =
531             object : DelegatingNode() {
532                 val draw = delegate(DrawMod())
533                 val layout = delegate(LayoutMod())
534                 val semantics = delegate(SemanticsMod())
535             }
536         val chain = layout(node)
537         chain.clearInvalidations()
538 
539         autoInvalidateNodeIncludingDelegates(node, 0.inv(), 1)
540 
541         assert(chain.drawInvalidated())
542         assert(chain.layoutInvalidated())
543         assert(chain.semanticsInvalidated())
544     }
545 
546     @Test
testNestedNodeInvalidationnull547     fun testNestedNodeInvalidation() {
548         val node =
549             object : DelegatingNode() {
550                 val wrapped = delegate(DelegatedWrapper { DelegatedWrapper { DrawMod() } })
551             }
552         val chain = layout(node)
553         chain.clearInvalidations()
554 
555         autoInvalidateNodeIncludingDelegates(node, 0.inv(), 1)
556 
557         assert(chain.drawInvalidated())
558         assert(!chain.layoutInvalidated())
559         assert(!chain.semanticsInvalidated())
560     }
561 
562     @Test
testDelegateUndelegateCausesInvalidationsForDelegateKindsOnlynull563     fun testDelegateUndelegateCausesInvalidationsForDelegateKindsOnly() {
564         val node =
565             object : DelegatingNode() {
566                 val semantics = delegate(SemanticsMod())
567             }
568         val chain = layout(node)
569         chain.clearInvalidations()
570 
571         val draw = node.delegateUnprotected(DrawMod())
572         assert(chain.drawInvalidated())
573         assert(!chain.semanticsInvalidated())
574 
575         chain.clearInvalidations()
576         node.undelegateUnprotected(draw)
577 
578         assert(chain.drawInvalidated())
579         assert(!chain.semanticsInvalidated())
580     }
581 
582     @Test
testDelegatingToLayoutNodeUpdatesCoordinatorsnull583     fun testDelegatingToLayoutNodeUpdatesCoordinators() {
584         val a = DrawMod()
585         val b = object : DelegatingNode() {}
586         val c = LayoutMod()
587         layout(a, b, c)
588 
589         val aCoord = a.requireCoordinator(Nodes.Any)
590         val bCoord = b.requireCoordinator(Nodes.Any)
591         val cCoord = c.requireCoordinator(Nodes.Any)
592 
593         assert(cCoord === bCoord)
594         assert(cCoord === aCoord)
595 
596         val lm = b.delegateUnprotected(LayoutMod())
597 
598         assert(cCoord === c.requireCoordinator(Nodes.Any))
599         assert(cCoord !== b.requireCoordinator(Nodes.Any))
600         assert(cCoord !== a.requireCoordinator(Nodes.Any))
601 
602         assert(a.requireCoordinator(Nodes.Any) === b.requireCoordinator(Nodes.Any))
603 
604         b.undelegateUnprotected(lm)
605 
606         assert(c.requireCoordinator(Nodes.Any) === b.requireCoordinator(Nodes.Any))
607         assert(c.requireCoordinator(Nodes.Any) === a.requireCoordinator(Nodes.Any))
608     }
609 
610     @Test
testDelegateAttachDetachnull611     fun testDelegateAttachDetach() {
612         val a = object : DelegatingNode() {}
613         val b = object : DelegatingNode() {}
614         val c = DrawMod()
615         a.delegateUnprotected(b)
616         b.delegateUnprotected(c)
617 
618         // not attached yet, but the nodes should all point to a
619         assert(!a.isAttached)
620         assert(!b.isAttached)
621         assert(!c.isAttached)
622 
623         assert(a.node === a)
624         assert(b.node === a)
625         assert(c.node === a)
626 
627         val chain = layout(a)
628 
629         // attached now, nodes should still point to a
630         assert(a.isAttached)
631         assert(b.isAttached)
632         assert(c.isAttached)
633 
634         assert(a.node === a)
635         assert(b.node === a)
636         assert(c.node === a)
637 
638         // detached now, nodes should still point to a
639         chain.runDetachLifecycle()
640         chain.markAsDetached()
641 
642         assert(!a.isAttached)
643         assert(!b.isAttached)
644         assert(!c.isAttached)
645 
646         assert(a.node === a)
647         assert(b.node === a)
648         assert(c.node === a)
649 
650         chain.markAsAttached()
651         chain.runAttachLifecycle()
652 
653         // attached now, nodes should still point to a
654         assert(a.isAttached)
655         assert(b.isAttached)
656         assert(c.isAttached)
657 
658         assert(a.node === a)
659         assert(b.node === a)
660         assert(c.node === a)
661 
662         b.undelegateUnprotected(c)
663         a.undelegateUnprotected(b)
664 
665         // delegates are detached. nodes should point to themselves
666         assert(a.isAttached)
667         assert(!b.isAttached)
668         assert(!c.isAttached)
669 
670         assert(a.node === a)
671         assert(b.node === b)
672         assert(c.node === c)
673     }
674 
675     @Test
testDelegateInAttachUndelegateInDetachnull676     fun testDelegateInAttachUndelegateInDetach() {
677         val b = DrawMod()
678         val a =
679             object : DelegatingNode() {
680                 override fun onAttach() {
681                     delegate(b)
682                 }
683 
684                 override fun onDetach() {
685                     undelegate(b)
686                 }
687             }
688 
689         // not attached yet or delegated yet
690         assert(!a.isAttached)
691         assert(!b.isAttached)
692 
693         assert(a.node === a)
694         assert(b.node === b)
695 
696         val chain = layout(a)
697 
698         // attached now, nodes should now point to a
699         assert(a.isAttached)
700         assert(b.isAttached)
701 
702         assert(a.node === a)
703         assert(b.node === a)
704 
705         chain.runDetachLifecycle()
706         chain.markAsDetached()
707 
708         // detached AND undelegated now
709         assert(!a.isAttached)
710         assert(!b.isAttached)
711 
712         assert(a.node === a)
713         assert(b.node === b)
714 
715         chain.markAsAttached()
716         chain.runAttachLifecycle()
717 
718         // attached and delegated now
719         assert(a.isAttached)
720         assert(b.isAttached)
721 
722         assert(a.node === a)
723         assert(b.node === a)
724     }
725 
726     @Test
testDelegateInAttachnull727     fun testDelegateInAttach() {
728         val b = DrawMod()
729         val a =
730             object : DelegatingNode() {
731                 override fun onAttach() {
732                     delegate(b)
733                 }
734             }
735 
736         // not attached yet or delegated yet
737         assert(!a.isAttached)
738         assert(!b.isAttached)
739 
740         assert(a.node === a)
741         assert(b.node === b)
742 
743         val chain = layout(a)
744 
745         // attached now, nodes should now point to a
746         assert(a.isAttached)
747         assert(b.isAttached)
748 
749         assert(a.node === a)
750         assert(b.node === a)
751 
752         chain.runDetachLifecycle()
753         chain.markAsDetached()
754 
755         // detached now, still delegated
756         assert(!a.isAttached)
757         assert(!b.isAttached)
758 
759         assert(a.node === a)
760         assert(b.node === a)
761 
762         chain.markAsAttached()
763         chain.runAttachLifecycle()
764 
765         // attached, still delegated
766         assert(a.isAttached)
767         assert(b.isAttached)
768 
769         assert(a.node === a)
770         assert(b.node === a)
771     }
772 }
773 
clearInvalidationsnull774 private fun NodeChain.clearInvalidations() {
775     val owner = layoutNode.owner
776     check(owner is MockOwner)
777     owner.onRequestMeasureParams.clear()
778     owner.invalidatedLayers.clear()
779     layoutNode.isSemanticsInvalidated = false
780     //    owner.semanticsChanged = false
781 }
782 
layoutInvalidatednull783 private fun NodeChain.layoutInvalidated(): Boolean {
784     val owner = layoutNode.owner
785     check(owner is MockOwner)
786     return owner.onRequestMeasureParams.isNotEmpty()
787 }
788 
drawInvalidatednull789 private fun NodeChain.drawInvalidated(): Boolean {
790     val owner = layoutNode.owner
791     check(owner is MockOwner)
792     return owner.invalidatedLayers.isNotEmpty()
793 }
794 
semanticsInvalidatednull795 private fun NodeChain.semanticsInvalidated(): Boolean {
796     return layoutNode.isSemanticsInvalidated
797 }
798 
layoutnull799 internal fun layout(
800     vararg modifiers: Modifier.Node,
801     block: LayoutScope.() -> Unit = {}
802 ): NodeChain {
803     val owner = MockOwner()
804     val root = LayoutNode()
805     val ln = LayoutNode()
806     root.insertAt(0, ln)
807     root.attach(owner)
808     var m: Modifier = Modifier
809     for (node in modifiers) {
810         m = m.then(NodeElement(node))
811     }
812     ln.nodes.updateFrom(m)
813     LayoutScopeImpl(ln).block()
<lambda>null814     root.innerCoordinator.updateLayerBlock({})
815     return ln.nodes
816 }
817 
818 internal data class NodeElement(val node: Modifier.Node) : ModifierNodeElement<Modifier.Node>() {
createnull819     override fun create(): Modifier.Node = node
820 
821     override fun update(node: Modifier.Node) {}
822 }
823 
824 class Recorder : (Any) -> Unit {
825     val recorded = mutableListOf<Any>()
826 
invokenull827     override fun invoke(p1: Any) {
828         recorded.add(p1)
829     }
830 }
831 
832 internal class LayoutScopeImpl(val layout: LayoutNode) : LayoutScope {
layoutnull833     override fun layout(vararg modifiers: Modifier.Node, block: LayoutScope.() -> Unit) {
834         val ln = LayoutNode()
835         layout.insertAt(layout.children.size, ln)
836         var m: Modifier = Modifier
837         for (node in modifiers) {
838             m = m.then(NodeElement(node))
839         }
840         ln.nodes.updateFrom(m)
841         LayoutScopeImpl(ln).block()
842     }
843 }
844 
845 interface LayoutScope {
layoutnull846     fun layout(vararg modifiers: Modifier.Node) = layout(*modifiers) {}
847 
layoutnull848     fun layout(vararg modifiers: Modifier.Node, block: LayoutScope.() -> Unit)
849 }
850 
851 internal inline fun <reified T> assertDispatchOrder(
852     node: Modifier.Node,
853     kind: NodeKind<T>,
854     vararg expected: T
855 ) {
856     val dispatches = mutableListOf<T>()
857     node.dispatchForKind(kind) { dispatches.add(it) }
858     assertThat(dispatches.toTypedArray()).isEqualTo(expected)
859 }
860 
861 class DrawMod(val id: String = "") : DrawModifierNode, Modifier.Node() {
drawnull862     override fun ContentDrawScope.draw() {}
863 
toStringnull864     override fun toString(): String {
865         return "DrawMod($id)"
866     }
867 }
868 
869 class SemanticsMod(val id: String = "") : SemanticsModifierNode, Modifier.Node() {
applySemanticsnull870     override fun SemanticsPropertyReceiver.applySemantics() {}
871 
toStringnull872     override fun toString(): String {
873         return "SemanticsMod($id)"
874     }
875 }
876 
877 class LayoutMod(val id: String = "") : LayoutModifierNode, Modifier.Node() {
measurenull878     override fun MeasureScope.measure(
879         measurable: Measurable,
880         constraints: Constraints
881     ): MeasureResult {
882         val placeable = measurable.measure(constraints)
883         return layout(placeable.width, placeable.height) { placeable.place(0, 0) }
884     }
885 
toStringnull886     override fun toString(): String {
887         return "LayoutMod($id)"
888     }
889 }
890 
891 class DelegatedWrapper<T : Modifier.Node>(fn: () -> T) : DelegatingNode() {
892     val wrapped = delegate(fn())
893 
toStringnull894     override fun toString(): String = "Wrapped<$wrapped>"
895 }
896 
897 internal inline fun <reified T> Modifier.Node.asKind(kind: NodeKind<T>): T? {
898     if (!isKind(kind)) return null
899     if (this is T) return this
900     if (this is DelegatingNode) {
901         forEachDelegateBreadthFirst { if (it is T) return it }
902     }
903     return null
904 }
905 
forEachDelegateBreadthFirstnull906 internal inline fun DelegatingNode.forEachDelegateBreadthFirst(block: (Modifier.Node) -> Unit) {
907     var node: Modifier.Node? = delegate
908     var queue: ArrayDeque<Modifier.Node>? = null
909     while (node != null) {
910         block(node)
911         if (node is DelegatingNode) {
912             queue = queue.enqueue(node.delegate)
913         }
914         node = node.child ?: queue?.removeFirst()
915     }
916 }
917 
enqueuenull918 private fun ArrayDeque<Modifier.Node>?.enqueue(node: Modifier.Node?): ArrayDeque<Modifier.Node>? {
919     if (node == null) return this
920     val queue = this ?: ArrayDeque(8)
921     queue.addLast(node)
922     return queue
923 }
924