<lambda>null1 package android.platform.helpers.foldable
2
3 import android.graphics.Point
4 import android.platform.uiautomator_helpers.DeviceHelpers.uiDevice
5 import android.platform.uiautomator_helpers.WaitUtils.waitFor
6 import com.google.common.truth.Expect
7 import com.google.common.truth.Truth.assertWithMessage
8 import java.time.Duration
9
10 /**
11 * Utilities to write tests for moving icons towards the center, as happens during the fold
12 * animation.
13 */
14 object UnfoldAnimationTestingUtils {
15
16 /**
17 * Returns icon positions after any one of them is moved.
18 *
19 * [currentProvider] returned icons are compared with [initialIcons]. As soon as any moved, the
20 * new set is returned.
21 */
22 fun getIconPositionsAfterAnyIconMove(
23 initialIcons: Set<Icon>,
24 currentProvider: () -> Set<Icon>
25 ): Set<Icon> {
26 return waitFor("Icons moving", Duration.ofSeconds(20)) {
27 val newIcons = currentProvider()
28 val moved =
29 commonIcons(initialIcons, newIcons).any { (initial, new) ->
30 initial.position != new.position
31 }
32 return@waitFor if (moved) newIcons else null
33 }
34 }
35
36 /**
37 * Waits until the positions returned by [currentProvider] match [expected].
38 *
39 * Only common icons between the 2 sets are considered. Thows if there is no intersection.
40 */
41 fun waitForPositionsToMatch(expected: Set<Icon>, currentProvider: () -> Set<Icon>) {
42 waitFor("Icons matching expected positions") {
43 val allMoved =
44 commonIcons(expected, currentProvider()).all { (expected, current) ->
45 expected.position == current.position
46 }
47 return@waitFor if (allMoved) true else null
48 }
49 }
50
51 /** Returns icons in both [setA] and [setB], comparing only icon name. */
52 fun commonIcons(setA: Set<Icon>, setB: Set<Icon>): Set<Pair<Icon, Icon>> {
53 return commonNames(setA, setB)
54 .also {
55 assertWithMessage("Empty intersection between $setA and $setB")
56 .that(it)
57 .isNotEmpty()
58 }
59 .map { name ->
60 val aIcon = setA.findWithName(name)
61 val bIcon = setB.findWithName(name)
62 aIcon to bIcon
63 }
64 .toSet()
65 }
66
67 private fun commonNames(setA: Set<Icon>, setB: Set<Icon>): Set<String> {
68 return setA.map { it.name }.intersect(setB.map { it.name }.toSet())
69 }
70
71 private fun Set<Icon>.findWithName(iconName: String): Icon {
72 return find { it.name == iconName } ?: error("Icon with name $iconName not found in $this")
73 }
74
75 /**
76 * Asserts [coordinate] (x or y)coordinate of [new] icon has moved towards the center compared
77 * to [old].
78 */
79 fun assertIconMovedTowardsTheCenter(old: Icon, new: Icon, expect: Expect, axis: Axis) {
80 assertWithMessage("Comparing icons with different names").that(old.name).isEqualTo(new.name)
81 val oldPosition = if (axis == Axis.X) old.position.x else old.position.y
82 val newPosition = if (axis == Axis.X) new.position.x else new.position.y
83 val expectThatOld =
84 expect.withMessage("Icon: ${old.name} didn't move towards the center").that(oldPosition)
85 if (oldPosition < uiDevice.displayWidth / 2) {
86 expectThatOld.isLessThan(newPosition)
87 } else {
88 expectThatOld.isGreaterThan(newPosition)
89 }
90 }
91
92 /** Describes axis (x or y) of a View */
93 enum class Axis {
94 X,
95 Y
96 }
97
98 /**
99 * Represent a UI element with a position, used in [UnfoldAnimationTestingUtils] for foldable
100 * animation testing.
101 */
102 data class Icon(val name: String, val position: Point)
103 }
104