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